diff --git a/.cirrus.yml b/.cirrus.yml index 6b2fcd50..8aa67a0f 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -2,26 +2,115 @@ env: CIRRUS_CLONE_DEPTH: "20" CIRRUS_SHELL: bash + ARTIFACTORY_PRIVATE_USERNAME: vault-${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-private-reader + ARTIFACTORY_PRIVATE_PASSWORD: VAULT[development/artifactory/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-private-reader access_token] + ARTIFACTORY_ACCESS_TOKEN: VAULT[development/artifactory/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-private-reader access_token] + + GIT_SUB_MODULE: "" + GRADLE_USER_HOME: ${CIRRUS_WORKING_DIR}/.gradle + +linux_image_template: &LINUX_IMAGE + image: ${CIRRUS_AWS_ACCOUNT}.dkr.ecr.eu-central-1.amazonaws.com/base:j17-g7-latest + cluster_name: ${CIRRUS_CLUSTER_NAME} + region: eu-central-1 + namespace: default + use_in_memory_disk: true + +linux_3_5_cpu_7G_template: &LINUX_3_5_CPU_7G + eks_container: + <<: *LINUX_IMAGE + cpu: 3.5 + memory: 7G + +linux_6_cpu_12G_java_17_template: &LINUX_6_CPU_12G_JAVA_17 + eks_container: + <<: *LINUX_IMAGE + image: ${CIRRUS_AWS_ACCOUNT}.dkr.ecr.eu-central-1.amazonaws.com/base:j17-g7-latest + cpu: 6 + memory: 12G + eks_container: &CONTAINER_DEFINITION - image: ${CIRRUS_AWS_ACCOUNT}.dkr.ecr.eu-central-1.amazonaws.com/base:latest + image: ${CIRRUS_AWS_ACCOUNT}.dkr.ecr.eu-central-1.amazonaws.com/base:j17-g7-latest cluster_name: ${CIRRUS_CLUSTER_NAME} region: eu-central-1 namespace: default +## Build tasks + +setup_gradle_cache_template: &SETUP_GRADLE_CACHE + gradle_cache: + folder: .gradle/caches + create_gradle_directory_script: + - mkdir -p "${CIRRUS_WORKING_DIR}/.gradle" + +cleanup_gradle_cache_script_template: &CLEANUP_GRADLE_CACHE_SCRIPT + cleanup_gradle_script: + - /usr/bin/find "${CIRRUS_WORKING_DIR}/.gradle/caches/" -name "*.lock" -type f -delete + - rm -rf "${CIRRUS_WORKING_DIR}/.gradle/caches/4.10.2/" + - rm -rf "${CIRRUS_WORKING_DIR}/.gradle/caches/journal-1/" + - rm -rf "${CIRRUS_WORKING_DIR}/.gradle/caches/build-cache-1/" + build_task: - eks_container: - <<: *CONTAINER_DEFINITION - cpu: 1 - memory: 1G + <<: *LINUX_3_5_CPU_7G + <<: *SETUP_GRADLE_CACHE env: - # Possible values for ARTIFACTORY_DEPLOY_REPO: sonarsource-private-qa, sonarsource-public-qa - ARTIFACTORY_DEPLOY_REPO: FIXME + ARTIFACTORY_DEPLOY_REPO: sonarsource-public-qa ARTIFACTORY_DEPLOY_USERNAME: VAULT[development/artifactory/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-qa-deployer username] ARTIFACTORY_DEPLOY_PASSWORD: VAULT[development/artifactory/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-qa-deployer access_token] DEPLOY_PULL_REQUEST: "true" build_script: - source cirrus-env BUILD - - regular_build_... + - ./gradlew build + <<: *CLEANUP_GRADLE_CACHE_SCRIPT + +### QA tasks + +only_if_sonarsource_qa_template: &ONLY_IF_SONARSOURCE_QA + only_if: $CIRRUS_USER_COLLABORATOR == 'true' && $CIRRUS_TAG == "" && ($CIRRUS_PR != "" || $CIRRUS_BRANCH == "master" || $CIRRUS_BRANCH =~ "branch-.*" || $CIRRUS_BRANCH =~ "dogfood-on-.*") + +qa_task_filter_template: &QA_TASK_FILTER + depends_on: + - build + <<: *ONLY_IF_SONARSOURCE_QA + +gradle_its_template: &GRADLE_ITS_TEMPLATE + <<: *SETUP_GRADLE_CACHE + run_its_script: + - | + if [ -n "${GIT_SUB_MODULE}" ]; then + git submodule update --init --depth 1 "${GIT_SUB_MODULE}" + fi + - source cirrus-env QA + - ./gradlew build -x test + - ./gradlew "${GRADLE_TASK}" "-P${ITS_PROJECT}" + "-Dsonar.runtimeVersion=${SQ_VERSION}" + "-Dorchestrator.artifactory.accessToken=${ARTIFACTORY_ACCESS_TOKEN}" + -I "${GRADLE_HOME}/init.d/repoxAuth.init.gradle.kts" + -Pqa --info --stacktrace --console plain --no-daemon --build-cache + <<: *CLEANUP_GRADLE_CACHE_SCRIPT + +qa_plugin_task: + <<: *QA_TASK_FILTER + env: + ITS_PROJECT: "plugin" + GRADLE_TASK: ":its:plugin:test" + matrix: + - SQ_VERSION: "DEV" + - SQ_VERSION: "LATEST_RELEASE" + <<: *LINUX_6_CPU_12G_JAVA_17 + <<: *GRADLE_ITS_TEMPLATE + +qa_ruling_task: + <<: *QA_TASK_FILTER + env: + SQ_VERSION: "LATEST_RELEASE" + GRADLE_TASK: ":its:ruling:test" + ITS_PROJECT: "ruling" + GIT_SUB_MODULE: "its/sources" + <<: *LINUX_6_CPU_12G_JAVA_17 + <<: *GRADLE_ITS_TEMPLATE + +### Promote tasks promote_task: depends_on: @@ -30,8 +119,9 @@ promote_task: <<: *CONTAINER_DEFINITION cpu: 1 memory: 1G - env: - ARTIFACTORY_PROMOTE_ACCESS_TOKEN: VAULT[development/artifactory/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-promoter access_token] - GITHUB_TOKEN: VAULT[development/github/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-promotion token] - ARTIFACTS: FIXME # This was for Burgr links, is it still required? - script: cirrus_promote_... + #env: + #ARTIFACTORY_PROMOTE_ACCESS_TOKEN: VAULT[development/artifactory/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-promoter access_token] + #GITHUB_TOKEN: VAULT[development/github/token/${CIRRUS_REPO_OWNER}-${CIRRUS_REPO_NAME}-promotion token] + #ARTIFACTS: FIXME # This was for Burgr links, is it still required? + script: + - echo "Promoting artifacts" diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..dabd6815 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +* text eol=lf +*.bat -text +*.jar -text +*.cls -text +*.trigger -text +its/sources/** -text +its/plugin/projects/** -text +**/src/test/resources/** -text + diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 02ff9c18..d8ef1963 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1 @@ -# https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners -.github/CODEOWNERS @sonarsource/analysis-jvm-squad +.github/CODEOWNERS @SonarSource/analysis-jvm-squad diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..036ab0fc --- /dev/null +++ b/.gitignore @@ -0,0 +1,43 @@ +# --- Gradle +.gradle/ +build/ + +# ---- Maven +target/ +dependency-reduced-pom.xml + +# ---- SonarQube analysis +.scannerwork/ +.sonar/ + +# ---- IntelliJ IDEA +*.iws +*.iml +*.ipr +.idea/ +out/ +bin/ + +# ---- Eclipse +.classpath +.project +.settings + +# ---- Mac OS X +.DS_Store +Icon? +# Thumbnails +._* +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +# ---- Windows +# Windows image file caches +Thumbs.db +# Folder config file +Desktop.ini + +.java-version +test-report.out + diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e0166e17 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "its/sources"] + path = its/sources + url = https://github.com/SonarSource/slang-test-sources.git diff --git a/LICENSE_HEADER b/LICENSE_HEADER new file mode 100644 index 00000000..e18b2319 --- /dev/null +++ b/LICENSE_HEADER @@ -0,0 +1,19 @@ +/* + * SonarSource Go + * Copyright (C) 2018-$YEAR SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ diff --git a/README.md b/README.md index 64da6471..529b2a39 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,47 @@ -# sonar-go +# Sonar-Go -Go Analyzer +[![Build Status](https://api.cirrus-ci.com/github/SonarSource/slang.svg?branch=master)](https://cirrus-ci.com/github/SonarSource/slang) +[![Quality Gate](https://sonarcloud.io/api/project_badges/measure?project=org.sonarsource.slang%3Aslang&metric=alert_status)](https://sonarcloud.io/dashboard?id=org.sonarsource.slang%3Aslang) +[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=org.sonarsource.slang%3Aslang&metric=coverage)](https://sonarcloud.io/component_measures/domain/Coverage?id=org.sonarsource.slang%3Aslang) + +This is a developer documentation. If you want to analyze source code in SonarQube read one of the following documentation: + +We use the native Go parser to parse the Go language. + +## Have questions or feedback? + +To provide feedback (request a feature, report a bug, etc.) use the [SonarQube Community Forum](https://community.sonarsource.com/). Please do not forget to specify the language, plugin version, and SonarQube version. + +## Building + +### Setup + +If you are on Windows, read the [sonar-go-to-slang/README.md](sonar-go-to-slang/README.md) instructions. + + +### Build +Build and run Unit Tests: + + ./gradlew build + +## Integration Tests + +By default, Integration Tests (ITs) are skipped during builds. +If you want to run them, you need first to retrieve the related projects which are used as input: + + git submodule update --init its/sources + +Then build and run the Integration Tests using the `its` property: + + ./gradlew build -Pits --info --no-daemon + +You can also build and run only Ruling Tests using the `ruling` property: + + ./gradlew build -Pruling --info --no-daemon + +## License headers + +License headers are automatically updated by the spotless plugin but only for Java files. +Furthermore, there are files such as `package-info.java` and `module-info.java` that spotless ignores. +Also the Go source files are not handled. For those files use a manual script like below to update the license: + `find . -type f -name "*.go" -exec sed -i '' 's/2018-2023/2018-2024/' "{}" \;` diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..9e0d94ff --- /dev/null +++ b/build.gradle @@ -0,0 +1,283 @@ +import java.util.jar.JarInputStream + +plugins { + id 'java' + id 'jacoco' + id 'com.jfrog.artifactory' version '4.28.2' + id 'io.spring.dependency-management' version '1.0.6.RELEASE' apply false + id 'org.sonarqube' version '3.5.0.2730' + id 'de.thetaphi.forbiddenapis' version '3.0' apply false + id 'com.diffplug.spotless' version '6.15.0' +} + +allprojects { + apply plugin: 'java' + apply plugin: 'jacoco' + apply plugin: 'io.spring.dependency-management' + apply plugin: 'com.jfrog.artifactory' + apply plugin: 'maven-publish' + apply plugin: 'signing' + + gradle.projectsEvaluated { + tasks.withType(JavaCompile).configureEach { + if (project.hasProperty('warn')) { + options.compilerArgs << "-Xlint:unchecked" << "-Xlint:deprecation" + } else { + options.compilerArgs << "-Xlint:-unchecked" << "-Xlint:-deprecation" + } + } + } + + ext { + buildNumber = System.getProperty("buildNumber") + analyzerCommonsVersion = '2.7.0.1482' + pluginApiVersion = '10.1.0.809' + // slf4j is provided by SQ, SC or SL, should be aligned with sonar-plugin-api + slf4jApiVersion = '1.7.30' + sonarqubeVersion = '10.0.0.68432' + orchestratorVersion = '3.42.0.312' + sonarlintVersion = '9.0.0.74282' + sonarLinksCi = 'https://cirrus-ci.com/github/SonarSource/sonar-go' + sonarLinksScm = 'https://github.com/SonarSource/sonar-go' + + artifactsToPublish = '' + artifactsToDownload = '' + } + // Replaces the version defined in sources, usually x.y-SNAPSHOT, by a version identifying the build. + if (version.endsWith('-SNAPSHOT') && ext.buildNumber != null) { + def versionSuffix = (version.toString().count('.') == 1 ? ".0.${ext.buildNumber}" : ".${ext.buildNumber}") + version = version.replace('-SNAPSHOT', versionSuffix) + } + + + repositories { + mavenLocal() + mavenCentral() + } +} + +subprojects { + // do not publish to Artifactory by default + artifactoryPublish.skip = true + + apply plugin: 'io.spring.dependency-management' + apply plugin: 'com.diffplug.spotless' + dependencyManagement { + dependencies { + dependency "org.sonarsource.api.plugin:sonar-plugin-api:${pluginApiVersion}" + dependency "org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures:${pluginApiVersion}" + dependency "org.sonarsource.sonarqube:sonar-plugin-api-impl:${sonarqubeVersion}" + dependency "org.slf4j:slf4j-api:${slf4jApiVersion}" + dependency "ch.qos.logback:logback-classic:1.2.9" + dependency "org.sonarsource.sonarqube:sonar-ws:${sonarqubeVersion}" + dependency 'com.google.code.findbugs:jsr305:3.0.2' + dependency 'com.eclipsesource.minimal-json:minimal-json:0.9.5' + dependency "org.junit.jupiter:junit-jupiter-api:5.10.0" + dependency "org.junit.jupiter:junit-jupiter-engine:5.10.0" + dependency 'junit:junit:4.13.2' + dependency 'org.mockito:mockito-core:5.5.0' + dependency 'org.assertj:assertj-core:3.24.2' + dependency 'io.github.classgraph:classgraph:4.8.162' + dependency "org.sonarsource.analyzer-commons:sonar-analyzer-test-commons:${analyzerCommonsVersion}" + dependency "org.sonarsource.analyzer-commons:sonar-analyzer-commons:${analyzerCommonsVersion}" + dependency "org.sonarsource.analyzer-commons:sonar-xml-parsing:${analyzerCommonsVersion}" + dependency "org.sonarsource.orchestrator:sonar-orchestrator:${orchestratorVersion}" + dependency "org.sonarsource.sonarlint.core:sonarlint-core:${sonarlintVersion}" + dependency "javax.annotation:javax.annotation-api:1.3.2" + } + } + + java.sourceCompatibility = JavaVersion.VERSION_11 + tasks.withType(JavaCompile) { + options.encoding = "UTF-8" + options.release = java.sourceCompatibility.majorVersion as int + } + + jacoco { + toolVersion = "0.8.7" + } + + jacocoTestReport { + reports { + xml.required = true + csv.required = false + html.required = false + } + } + + // when subproject has Jacoco pLugin applied we want to generate XML report for coverage + plugins.withType(JacocoPlugin) { + tasks["test"].finalizedBy 'jacocoTestReport' + } + + configurations { + // include compileOnly dependencies during test + testCompile.extendsFrom compileOnly + } + + if (!project.path.startsWith(":its")) { + test { + useJUnitPlatform() + } + } + + test { + testLogging { + exceptionFormat 'full' // log the full stack trace (default is the 1st line of the stack trace) + events "skipped", "failed" + // verbose log for failed and skipped tests (by default the name of the tests are not logged) + } + def propKeys = System.properties.findAll { + it.key.startsWith("orchestrator") || it.key.startsWith("sonar") || it.key == "buildNumber" || it.key == "slangVersion" + }.collect { it.key } + systemProperties = System.properties.subMap(propKeys) + if (propKeys.contains("buildNumber") && !propKeys.contains("slangVersion")) { + systemProperties["slangVersion"] = version + } + } + + task sourcesJar(type: Jar, dependsOn: classes) { + classifier = 'sources' + from sourceSets.main.allSource + } + + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir + } + + // license updater + spotless { + java { + targetExclude "**/src/test/resources/**", "**/build/**", "its/sources/**", "its/plugin/projects/**" + licenseHeaderFile(rootProject.file("LICENSE_HEADER")).updateYearWithLatest(true) + } + } + + publishing { + publications { + mavenJava(MavenPublication) { + pom { + name = projectTitle + description = project.description + url = 'http://www.sonarqube.org/' + organization { + name = 'SonarSource' + url = 'http://www.sonarsource.com' + } + licenses { + license { + name = 'GNU LGPL 3' + url = 'http://www.gnu.org/licenses/lgpl.txt' + distribution = 'repo' + } + } + scm { + url = 'https://github.com/SonarSource/sonar-go' + } + developers { + developer { + id = 'sonarsource-team' + name = 'SonarSource Team' + } + } + } + } + } + } + + signing { + def signingKeyId = findProperty("signingKeyId") + def signingKey = findProperty("signingKey") + def signingPassword = findProperty("signingPassword") + useInMemoryPgpKeys(signingKeyId, signingKey, signingPassword) + required { + def branch = System.getenv()["CIRRUS_BRANCH"] + return (branch == 'master' || branch ==~ 'branch-[\\d.]+') && + gradle.taskGraph.hasTask(":artifactoryPublish") + } + sign publishing.publications + } + + tasks.withType(Sign) { + onlyIf { + def branch = System.getenv()["CIRRUS_BRANCH"] + return !artifactoryPublish.skip && + (branch == 'master' || branch ==~ 'branch-[\\d.]+') && + gradle.taskGraph.hasTask(":artifactoryPublish") + } + } +} + +sonar { + properties { + property 'sonar.projectName', projectTitle + property 'sonar.links.ci', "${sonarLinksCi}" + property 'sonar.links.scm', "${sonarLinksScm}" + property 'sonar.links.issue', 'https://jira.sonarsource.com/browse/SONARGO' + property 'sonar.exclusions', '**/build/**/*' + } +} + +artifactory { + clientConfig.setIncludeEnvVars(true) + clientConfig.setEnvVarsExcludePatterns('*password*,*PASSWORD*,*secret*,*MAVEN_CMD_LINE_ARGS*,sun.java.command,*token*,*TOKEN*,*LOGIN*,*login*,*key*,*KEY*,*PASSPHRASE*,*signing*') + contextUrl = System.getenv('ARTIFACTORY_URL') + publish { + repository { + repoKey = System.getenv('ARTIFACTORY_DEPLOY_REPO') + username = System.getenv('ARTIFACTORY_DEPLOY_USERNAME') + password = System.getenv('ARTIFACTORY_DEPLOY_PASSWORD') + } + defaults { + properties = [ + 'build.name' : 'sonar-go', + 'build.number' : System.getenv('BUILD_NUMBER'), + 'pr.branch.target': System.getenv('PULL_REQUEST_BRANCH_TARGET'), + 'pr.number' : System.getenv('PULL_REQUEST_NUMBER'), + 'vcs.branch' : System.getenv('GIT_BRANCH'), + 'vcs.revision' : System.getenv('GIT_COMMIT'), + 'version' : version + ] + publications('mavenJava') + publishPom = true + publishIvy = false + } + } + + clientConfig.info.setBuildName('sonar-go') + clientConfig.info.setBuildNumber(System.getenv('BUILD_NUMBER')) + // Define the artifacts to be deployed to https://binaries.sonarsource.com on releases + clientConfig.info.addEnvironmentProperty('ARTIFACTS_TO_PUBLISH', artifactsToPublish) + clientConfig.info.addEnvironmentProperty('ARTIFACTS_TO_DOWNLOAD', artifactsToDownload) + // The name of this variable is important because it's used by the delivery process when extracting version from Artifactory build info. + clientConfig.info.addEnvironmentProperty('PROJECT_VERSION', "${version}") +} + +void enforceJarSizeAndCheckContent(File file, long minSize, long maxSize) { + long size = file.length() + if (size < minSize) { + throw new GradleException("${file.path} size ($size) too small. Min is $minSize") + } else if (size > maxSize) { + throw new GradleException("${file.path} size ($size) too large. Max is $maxSize") + } + checkJarEntriesPathUniqueness file +} + +// A jar should not contain 2 entries with the same path, furthermore Pack200 will fail to unpack it +void checkJarEntriesPathUniqueness(File file) { + def allNames = new HashSet() + def duplicatedNames = new HashSet() + file.withInputStream { input -> + new JarInputStream(input).withCloseable { jarInput -> + for (def jarEntry = jarInput.nextJarEntry; jarEntry != null; jarEntry = jarInput.nextJarEntry) { + if (!allNames.add(jarEntry.name)) { + duplicatedNames.add jarEntry.name + } + } + } + } + if (!duplicatedNames.empty) { + throw new GradleException("Duplicated entries in the jar: '${file.path}': ${duplicatedNames.join(', ')}") + } +} diff --git a/checkstyle-import/build.gradle b/checkstyle-import/build.gradle new file mode 100644 index 00000000..4c40ff92 --- /dev/null +++ b/checkstyle-import/build.gradle @@ -0,0 +1,11 @@ +dependencies { + implementation 'com.google.code.findbugs:jsr305' + compileOnly 'org.sonarsource.api.plugin:sonar-plugin-api' + testImplementation 'org.sonarsource.sonarqube:sonar-plugin-api-impl' + implementation 'org.sonarsource.analyzer-commons:sonar-analyzer-commons' + implementation 'org.sonarsource.analyzer-commons:sonar-xml-parsing' + testImplementation project(':slang-testing') + testImplementation "org.junit.jupiter:junit-jupiter-api" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + testImplementation 'org.assertj:assertj-core' +} diff --git a/checkstyle-import/src/main/java/org/sonarsource/slang/externalreport/CheckstyleFormatImporter.java b/checkstyle-import/src/main/java/org/sonarsource/slang/externalreport/CheckstyleFormatImporter.java new file mode 100644 index 00000000..03fe5423 --- /dev/null +++ b/checkstyle-import/src/main/java/org/sonarsource/slang/externalreport/CheckstyleFormatImporter.java @@ -0,0 +1,210 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.externalreport; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import javax.annotation.Nullable; +import javax.xml.namespace.QName; +import javax.xml.stream.XMLEventReader; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.events.Attribute; +import javax.xml.stream.events.StartElement; +import javax.xml.stream.events.XMLEvent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.issue.NewExternalIssue; +import org.sonar.api.batch.sensor.issue.NewIssueLocation; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleType; +import org.sonarsource.analyzer.commons.xml.SafeStaxParserFactory; + +/** + * Import external linter reports having a "Checkstyle" xml format into SonarQube + */ +public class CheckstyleFormatImporter { + + private static final Logger LOG = LoggerFactory.getLogger(CheckstyleFormatImporter.class); + + private static final Long DEFAULT_CONSTANT_DEBT_MINUTES = 5L; + + private static final QName CHECKSTYLE = new QName("checkstyle"); + private static final QName FILE = new QName("file"); + private static final QName ERROR = new QName("error"); + private static final QName NAME = new QName("name"); + private static final QName SEVERITY = new QName("severity"); + private static final QName SOURCE = new QName("source"); + private static final QName LINE = new QName("line"); + private static final QName MESSAGE = new QName("message"); + + private final SensorContext context; + + protected final String linterKey; + + private int level = 0; + + @Nullable + private InputFile inputFile = null; + + /** + * @param context, the context where issues will be sent + * @param linterKey, used to specify the rule repository + */ + public CheckstyleFormatImporter(SensorContext context, String linterKey) { + this.context = context; + this.linterKey = linterKey; + } + + /** + * "importFile" parses the given report file and imports the content into SonarQube + * @param reportPath, path of the xml file + */ + public void importFile(File reportPath) { + try (InputStream in = new FileInputStream(reportPath)) { + XMLEventReader reader = SafeStaxParserFactory.createXMLInputFactory().createXMLEventReader(in); + level = 0; + while (reader.hasNext()) { + XMLEvent event = reader.nextEvent(); + if (event.isStartElement()) { + level++; + onElement(event.asStartElement()); + } else if (event.isEndElement()) { + level--; + } + } + } catch (IOException | XMLStreamException | RuntimeException e) { + LOG.error("No issue information will be saved as the report file '{}' can't be read.", reportPath, e); + } + } + + private void onElement(StartElement element) throws IOException { + if (level == 1 && !CHECKSTYLE.equals(element.getName())) { + throw new IOException("Unexpected document root '" + element.getName().getLocalPart() + "' instead of 'checkstyle'."); + } else if (level == 2 && FILE.equals(element.getName())) { + onFileElement(element); + } else if (level == 3 && ERROR.equals(element.getName()) && inputFile != null) { + onErrorElement(element); + } + } + + private void onFileElement(StartElement element) { + String filePath = getAttributeValue(element, NAME); + if (filePath.isEmpty()) { + inputFile = null; + return; + } + FilePredicates predicates = context.fileSystem().predicates(); + inputFile = context.fileSystem().inputFile(predicates.or( + predicates.hasAbsolutePath(filePath), + predicates.hasRelativePath(filePath))); + if (inputFile == null) { + LOG.warn("No input file found for {}. No {} issues will be imported on this file.", filePath, linterKey); + } + } + + private void onErrorElement(StartElement element) { + String source = getAttributeValue(element, SOURCE); + String line = getAttributeValue(element, LINE); + // severity could be: error, warning, info + String severity = getAttributeValue(element, SEVERITY); + String message = getAttributeValue(element, MESSAGE); + if (message.isEmpty()) { + LOG.debug("Unexpected error without any message for rule: '{}'", source); + return; + } + saveIssue(line, severity, source, message); + } + + private void saveIssue(String line, String severity, String source, String message) { + RuleType ruleType = ruleType(severity, source); + Severity ruleSeverity = severity(severity); + + RuleKey ruleKey = createRuleKey(source, ruleType, ruleSeverity); + + NewExternalIssue newExternalIssue = context.newExternalIssue() + .type(ruleType) + .severity(ruleSeverity) + .remediationEffortMinutes(effort(ruleKey.rule())); + + NewIssueLocation primaryLocation = newExternalIssue.newLocation() + .message(message) + .on(inputFile); + + if (!line.isEmpty()) { + primaryLocation.at(inputFile.selectLine(Integer.parseInt(line))); + } + + newExternalIssue + .at(primaryLocation) + .engineId(ruleKey.repository()) + .ruleId(ruleKey.rule()) + .save(); + } + + /** + * Return a RuleKey based on the source, RuleType and Severity of an issue. + */ + protected RuleKey createRuleKey(String source, RuleType ruleType, Severity ruleSeverity) { + return RuleKey.of(linterKey, source); + } + + /** + * Return a RuleType equivalent based on the different parameters. + * + * @param severity "severity" attribute's value of the report. Ex: "info", "error". + * @param source "source" attribute's value of the report. Ex: "gosec", "detekt.MagicNumber". + * @return the RuleType defined by the given parameters. + */ + protected RuleType ruleType(@Nullable String severity, String source) { + return "error".equals(severity) ? RuleType.BUG : RuleType.CODE_SMELL; + } + + /** + * Return a Severity equivalent based on the different parameters. + * + * @param severity "severity" attribute's value of the report. Ex: "info", "error". + * @return the Severity defined by the given parameters. + */ + protected Severity severity(@Nullable String severity) { + return "info".equals(severity) ? Severity.MINOR : Severity.MAJOR; + } + + /** + * Return an Effort value based on the ruleKey. + * + * @param ruleKey rule key of the current issue. + * @return the Effort defined by the given ruleKey. + */ + protected Long effort(String ruleKey) { + return DEFAULT_CONSTANT_DEBT_MINUTES; + } + + private static String getAttributeValue(StartElement element, QName attributeName) { + Attribute attribute = element.getAttributeByName(attributeName); + return attribute != null ? attribute.getValue() : ""; + } + +} diff --git a/checkstyle-import/src/main/java/org/sonarsource/slang/externalreport/package-info.java b/checkstyle-import/src/main/java/org/sonarsource/slang/externalreport/package-info.java new file mode 100644 index 00000000..f00cdb3e --- /dev/null +++ b/checkstyle-import/src/main/java/org/sonarsource/slang/externalreport/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonarsource.slang.externalreport; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/checkstyle-import/src/test/java/org/sonarsource/slang/externalreport/CheckstyleFormatImporterTest.java b/checkstyle-import/src/test/java/org/sonarsource/slang/externalreport/CheckstyleFormatImporterTest.java new file mode 100644 index 00000000..55fe6a5c --- /dev/null +++ b/checkstyle-import/src/test/java/org/sonarsource/slang/externalreport/CheckstyleFormatImporterTest.java @@ -0,0 +1,181 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.externalreport; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.api.rules.RuleType; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +class CheckstyleFormatImporterTest { + + static final Path PROJECT_DIR = Paths.get("src", "test", "resources", "externalreport"); + + static final String LINTER_KEY = "test-linter"; + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void import_detekt_issues() throws IOException { + List externalIssues = importIssues("detekt-checkstyle.xml"); + assertThat(externalIssues).hasSize(3); + + ExternalIssue first = externalIssues.get(0); + assertThat(first.primaryLocation().inputComponent().key()).isEqualTo("externalreport-project:main.kt"); + assertThat(first.ruleKey().rule()).isEqualTo("detekt.EmptyIfBlock"); + assertThat(first.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(first.severity()).isEqualTo(Severity.MINOR); + assertThat(first.primaryLocation().message()).isEqualTo("This empty block of code can be removed."); + assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(3); + + ExternalIssue second = externalIssues.get(1); + assertThat(second.primaryLocation().inputComponent().key()).isEqualTo("externalreport-project:main.kt"); + assertThat(second.ruleKey().rule()).isEqualTo("detekt.MagicNumber"); + assertThat(second.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(second.severity()).isEqualTo(Severity.MAJOR); + assertThat(second.remediationEffort().longValue()).isEqualTo(5L); + assertThat(second.primaryLocation().message()).isEqualTo("This expression contains a magic number. Consider defining it to a well named constant."); + assertThat(second.primaryLocation().textRange().start().line()).isEqualTo(3); + + ExternalIssue third = externalIssues.get(2); + assertThat(third.primaryLocation().inputComponent().key()).isEqualTo("externalreport-project:A.kt"); + assertThat(third.ruleKey().rule()).isEqualTo("detekt.EqualsWithHashCodeExist"); + assertThat(third.type()).isEqualTo(RuleType.BUG); + assertThat(third.severity()).isEqualTo(Severity.MAJOR); + assertThat(third.primaryLocation().message()).isEqualTo("A class should always override hashCode when overriding equals and the other way around."); + assertThat(third.primaryLocation().textRange().start().line()).isEqualTo(3); + + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void import_golandci_lint_issues() throws IOException { + List externalIssues = importIssues("golandci-lint-report.xml"); + assertThat(externalIssues).hasSize(1); + + ExternalIssue first = externalIssues.get(0); + assertThat(first.primaryLocation().inputComponent().key()).isEqualTo("externalreport-project:TabCharacter.go"); + assertThat(first.ruleKey().rule()).isEqualTo("deadcode"); + assertThat(first.type()).isEqualTo(RuleType.BUG); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + assertThat(first.primaryLocation().message()).isEqualTo("`three` is unused"); + assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(4); + + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void no_issues_with_invalid_report_path() throws IOException { + List externalIssues = importIssues("invalid-path.txt"); + assertThat(externalIssues).isEmpty(); + assertThat(onlyOneLogElement(logTester.logs(Level.ERROR))) + .startsWith("No issue information will be saved as the report file '") + .endsWith("invalid-path.txt' can't be read."); + } + + @Test + void no_issues_with_invalid_checkstyle_file() throws IOException { + List externalIssues = importIssues("not-checkstyle-file.xml"); + assertThat(externalIssues).isEmpty(); + assertThat(onlyOneLogElement(logTester.logs(Level.ERROR))) + .startsWith("No issue information will be saved as the report file '") + .endsWith("not-checkstyle-file.xml' can't be read."); + } + + @Test + void no_issues_with_invalid_xml_report() throws IOException { + List externalIssues = importIssues("invalid-file.xml"); + assertThat(externalIssues).isEmpty(); + assertThat(onlyOneLogElement(logTester.logs(Level.ERROR))) + .startsWith("No issue information will be saved as the report file '") + .endsWith("invalid-file.xml' can't be read."); + } + + @Test + void issues_when_xml_file_has_errors() throws IOException { + List externalIssues = importIssues("detekt-checkstyle-with-errors.xml"); + assertThat(externalIssues).hasSize(2); + + ExternalIssue first = externalIssues.get(0); + assertThat(first.primaryLocation().inputComponent().key()).isEqualTo("externalreport-project:main.kt"); + assertThat(first.ruleKey().rule()).isEqualTo("detekt.UnknownRuleKey"); + assertThat(first.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + assertThat(first.primaryLocation().message()).isEqualTo("Error at file level with an unknown rule key."); + assertThat(first.primaryLocation().textRange()).isNull(); + + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + assertThat(logTester.logs(Level.WARN)).containsExactlyInAnyOrder( + "No input file found for not-existing-file.kt. No test-linter issues will be imported on this file."); + assertThat(logTester.logs(Level.DEBUG)).containsExactlyInAnyOrder( + "Unexpected error without any message for rule: 'detekt.EmptyIfBlock'"); + } + + private List importIssues(String fileName) throws IOException { + SensorContextTester context = createContext(); + CheckstyleFormatImporter importer = new CheckstyleFormatImporter(context, LINTER_KEY); + importer.importFile(PROJECT_DIR.resolve(fileName).toAbsolutePath().toFile()); + return new ArrayList<>(context.allExternalIssues()); + } + + static SensorContextTester createContext() throws IOException { + SensorContextTester context = SensorContextTester.create(PROJECT_DIR); + Files.list(PROJECT_DIR) + .filter(file -> !Files.isDirectory(file)) + .forEach(file -> addFileToContext(context, PROJECT_DIR, file)); + return context; + } + + private static void addFileToContext(SensorContextTester context, Path projectDir, Path file) { + try { + String projectId = projectDir.getFileName().toString() + "-project"; + context.fileSystem().add(TestInputFileBuilder.create(projectId, projectDir.toFile(), file.toFile()) + .setCharset(UTF_8) + .setLanguage(file.toString().substring(file.toString().lastIndexOf('.') + 1)) + .setContents(new String(Files.readAllBytes(file), UTF_8)) + .setType(InputFile.Type.MAIN) + .build()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + static String onlyOneLogElement(List elements) { + assertThat(elements).hasSize(1); + return elements.get(0); + } + +} diff --git a/checkstyle-import/src/test/resources/externalreport/A.kt b/checkstyle-import/src/test/resources/externalreport/A.kt new file mode 100644 index 00000000..bdb14e56 --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/A.kt @@ -0,0 +1,7 @@ +package externalreport.detekt + +class A { + override fun equals(other: Any?): Boolean { + return super.equals(other) + } +} diff --git a/checkstyle-import/src/test/resources/externalreport/TabCharacter.go b/checkstyle-import/src/test/resources/externalreport/TabCharacter.go new file mode 100644 index 00000000..5f7884a0 --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/TabCharacter.go @@ -0,0 +1,7 @@ +// S105 +package samples + +func three() int { + x := 3 + return x // Noncompliant +} diff --git a/checkstyle-import/src/test/resources/externalreport/detekt-checkstyle-with-errors.xml b/checkstyle-import/src/test/resources/externalreport/detekt-checkstyle-with-errors.xml new file mode 100644 index 00000000..efcf34df --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/detekt-checkstyle-with-errors.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/checkstyle-import/src/test/resources/externalreport/detekt-checkstyle.xml b/checkstyle-import/src/test/resources/externalreport/detekt-checkstyle.xml new file mode 100644 index 00000000..35ba961d --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/detekt-checkstyle.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/checkstyle-import/src/test/resources/externalreport/golandci-lint-report.xml b/checkstyle-import/src/test/resources/externalreport/golandci-lint-report.xml new file mode 100644 index 00000000..2e91c57f --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/golandci-lint-report.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/checkstyle-import/src/test/resources/externalreport/invalid-file.xml b/checkstyle-import/src/test/resources/externalreport/invalid-file.xml new file mode 100644 index 00000000..18743226 --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/invalid-file.xml @@ -0,0 +1,2 @@ + +invalid xml file diff --git a/checkstyle-import/src/test/resources/externalreport/main.kt b/checkstyle-import/src/test/resources/externalreport/main.kt new file mode 100644 index 00000000..6562cc17 --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/main.kt @@ -0,0 +1,6 @@ +fun main(args: Array) { + val magicNumber = Integer.parseInt(args[0]) + if (magicNumber == 42) { + } + println("hello world") +} diff --git a/checkstyle-import/src/test/resources/externalreport/not-checkstyle-file.xml b/checkstyle-import/src/test/resources/externalreport/not-checkstyle-file.xml new file mode 100644 index 00000000..522312f6 --- /dev/null +++ b/checkstyle-import/src/test/resources/externalreport/not-checkstyle-file.xml @@ -0,0 +1,2 @@ + + diff --git a/checkstyle-import/src/test/resources/test-linter-rules.json b/checkstyle-import/src/test/resources/test-linter-rules.json new file mode 100644 index 00000000..d591c5e5 --- /dev/null +++ b/checkstyle-import/src/test/resources/test-linter-rules.json @@ -0,0 +1,38 @@ +[ + { + "key": "detekt.EmptyIfBlock", + "name": "Empty If Block", + "type": "CODE_SMELL", + "severity": "MINOR", + "description": "Empty block of code detected. As they serve no purpose they should be removed.", + "url": "https://arturbosch.github.io/detekt/empty-blocks.html#emptyifblock", + "tags": [ + "minor" + ], + "constantDebtMinutes": 5 + }, + { + "key": "detekt.MagicNumber", + "name": "Magic Number", + "type": "CODE_SMELL", + "severity": "INFO", + "description": "Report magic numbers. Magic number is a numeric literal that is not defined as a constant and hence it\u0027s unclear what the purpose of this number is. It\u0027s better to declare such numbers as constants and give them a proper name. By default, -1, 0, 1, and 2 are not considered to be magic numbers.", + "url": "https://arturbosch.github.io/detekt/style.html#magicnumber", + "tags": [ + "style" + ], + "constantDebtMinutes": 10 + }, + { + "key": "detekt.EqualsWithHashCodeExist", + "name": "Equals With Hash Code Exist", + "type": "CODE_SMELL", + "severity": "CRITICAL", + "description": "Always override hashCode when you override equals. All hash-based collections depend on objects meeting the equals-contract. Two equal objects must produce the same hashcode. When inheriting equals or hashcode, override the inherited and call the super method for clarification.", + "url": "https://arturbosch.github.io/detekt/potential-bugs.html#equalswithhashcodeexist", + "tags": [ + "defect" + ], + "constantDebtMinutes": 5 + } +] diff --git a/export_ws_variables_from_gradle b/export_ws_variables_from_gradle new file mode 100755 index 00000000..27ec8894 --- /dev/null +++ b/export_ws_variables_from_gradle @@ -0,0 +1,23 @@ +#! /usr/bin/env bash + +set -euox pipefail + +get_project_version() { + local version_property + version_property=$(./gradlew properties | grep --extended-regexp "^version: (.*)") + if [[ -z "${version_property}" ]]; then + echo "Could not find property version in project" >&2 + exit 2 + fi + local version + version=$(echo "${version_property}" | tr --delete "[:space:]" | cut --delimiter=":" --fields=2) + version="${version/-SNAPSHOT/}" + # Because the ws scan script expects a semver-like version (aa.bb.cc.XX), we append the build number to the project version. + if [[ "${version}" =~ ^[0-9]+\.[0-9]+$ ]]; then + version="${version}.0" + fi + version="${version}.${BUILD_NUMBER:-0}" + echo "${version}" +} + +export PROJECT_VERSION="$(get_project_version)" diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..21e96a89 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +group=org.sonarsource.slang +version=1.16.0-SNAPSHOT +description=Code Analyzer for Go +projectTitle=Go +org.gradle.jvmargs=-XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Xms256m -Xmx1024m diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..7454180f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..068cdb2d --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..1b6c7873 --- /dev/null +++ b/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..ac1b06f9 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/its/plugin/build.gradle b/its/plugin/build.gradle new file mode 100644 index 00000000..44943374 --- /dev/null +++ b/its/plugin/build.gradle @@ -0,0 +1,21 @@ +dependencies { + testImplementation 'org.sonarsource.sonarlint.core:sonarlint-core' + testImplementation 'org.sonarsource.orchestrator:sonar-orchestrator' + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.sonarsource.sonarqube:sonar-ws' + testImplementation 'org.sonarsource.analyzer-commons:sonar-analyzer-commons' +} + +sonarqube.skipProject = true + +test { + onlyIf { + project.hasProperty("plugin") || project.hasProperty("its") + } + filter { + includeTestsMatching 'org.sonarsource.slang.Tests' + includeTestsMatching 'org.sonarsource.slang.SonarLintTest' + } + systemProperty 'java.awt.headless', 'true' + outputs.upToDateWhen {false} +} diff --git a/its/plugin/projects/duplications/go/duplication/another.go b/its/plugin/projects/duplications/go/duplication/another.go new file mode 100644 index 00000000..2ab1c2fd --- /dev/null +++ b/its/plugin/projects/duplications/go/duplication/another.go @@ -0,0 +1,41 @@ +package samples + +import ( + "io" + "os" + "strings" + "bytes" +) + +var ascii_uppercase = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +var ascii_lowercase = []byte("abcdefghijklmnopqrstuvwxyz") +var ascii_uppercase_len = len(ascii_uppercase) +var ascii_lowercase_len = len(ascii_lowercase) +var ascii_allowed = []byte(" !") + +type rot13Reader struct { + r io.Reader +} + +func rot13(b byte) byte { + pos := bytes.IndexByte(ascii_uppercase, b) + if pos != -1 { + return ascii_uppercase[(pos+13)%ascii_uppercase_len] + } + pos = bytes.IndexByte(ascii_lowercase, b) + if pos != -1 { + return ascii_lowercase[(pos+13)%ascii_lowercase_len] + } + if bytes.IndexByte(ascii_allowed, b) == -1 { + panic("Invalid character") + } + return b +} + +func (r *rot13Reader) Read(b []byte) (n int, err error) { + n, err = r.r.Read(b) + for i := 0; i < n; i++ { + b[i] = rot13(b[i]) + } + return +} diff --git a/its/plugin/projects/duplications/go/duplication/pivot.go b/its/plugin/projects/duplications/go/duplication/pivot.go new file mode 100644 index 00000000..6942c337 --- /dev/null +++ b/its/plugin/projects/duplications/go/duplication/pivot.go @@ -0,0 +1,47 @@ +package samples + +import ( + "io" + "os" + "strings" + "bytes" +) + +var ascii_uppercase = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +var ascii_lowercase = []byte("abcdefghijklmnopqrstuvwxyz") +var ascii_uppercase_len = len(ascii_uppercase) +var ascii_lowercase_len = len(ascii_lowercase) +var ascii_allowed = []byte(" !") + +type rot13Reader struct { + r io.Reader +} + +func rot13(b byte) byte { + pos := bytes.IndexByte(ascii_uppercase, b) + if pos != -1 { + return ascii_uppercase[(pos+13)%ascii_uppercase_len] + } + pos = bytes.IndexByte(ascii_lowercase, b) + if pos != -1 { + return ascii_lowercase[(pos+13)%ascii_lowercase_len] + } + if bytes.IndexByte(ascii_allowed, b) == -1 { + panic("Invalid character") + } + return b +} + +func (r *rot13Reader) Read(b []byte) (n int, err error) { + n, err = r.r.Read(b) + for i := 0; i < n; i++ { + b[i] = rot13(b[i]) + } + return +} + +func main() { + s := strings.NewReader("Lbh penpxrq gur pbqr!") + r := rot13Reader{s} + io.Copy(os.Stdout, &r) +} diff --git a/its/plugin/projects/duplications/go/pivot.go b/its/plugin/projects/duplications/go/pivot.go new file mode 100644 index 00000000..6942c337 --- /dev/null +++ b/its/plugin/projects/duplications/go/pivot.go @@ -0,0 +1,47 @@ +package samples + +import ( + "io" + "os" + "strings" + "bytes" +) + +var ascii_uppercase = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +var ascii_lowercase = []byte("abcdefghijklmnopqrstuvwxyz") +var ascii_uppercase_len = len(ascii_uppercase) +var ascii_lowercase_len = len(ascii_lowercase) +var ascii_allowed = []byte(" !") + +type rot13Reader struct { + r io.Reader +} + +func rot13(b byte) byte { + pos := bytes.IndexByte(ascii_uppercase, b) + if pos != -1 { + return ascii_uppercase[(pos+13)%ascii_uppercase_len] + } + pos = bytes.IndexByte(ascii_lowercase, b) + if pos != -1 { + return ascii_lowercase[(pos+13)%ascii_lowercase_len] + } + if bytes.IndexByte(ascii_allowed, b) == -1 { + panic("Invalid character") + } + return b +} + +func (r *rot13Reader) Read(b []byte) (n int, err error) { + n, err = r.r.Read(b) + for i := 0; i < n; i++ { + b[i] = rot13(b[i]) + } + return +} + +func main() { + s := strings.NewReader("Lbh penpxrq gur pbqr!") + r := rot13Reader{s} + io.Copy(os.Stdout, &r) +} diff --git a/its/plugin/projects/externalreport/golangci-lint/README.md b/its/plugin/projects/externalreport/golangci-lint/README.md new file mode 100644 index 00000000..6b74ceb2 --- /dev/null +++ b/its/plugin/projects/externalreport/golangci-lint/README.md @@ -0,0 +1,8 @@ +# External Linter Reports + +## GolangCI-Lint + +The file `golangci-lint-checkstyle.xml` has been generated using the command: +``` +golangci-lint run --out-format checkstyle > golangci-lint-checkstyle.xml +``` diff --git a/its/plugin/projects/externalreport/golangci-lint/SelfAssignement.go b/its/plugin/projects/externalreport/golangci-lint/SelfAssignement.go new file mode 100644 index 00000000..377490bf --- /dev/null +++ b/its/plugin/projects/externalreport/golangci-lint/SelfAssignement.go @@ -0,0 +1,10 @@ +// S1656 +package samples + +type User struct { name string } + +func (user *User) rename(name string) { + name = name // Noncompliant {{Remove or correct this useless self-assignment.}} + user.name = name // Compliant + user.name = user.name // Noncompliant +} diff --git a/its/plugin/projects/externalreport/golangci-lint/golangci-lint-checkstyle.xml b/its/plugin/projects/externalreport/golangci-lint/golangci-lint-checkstyle.xml new file mode 100644 index 00000000..1fed52ef --- /dev/null +++ b/its/plugin/projects/externalreport/golangci-lint/golangci-lint-checkstyle.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/its/plugin/projects/externalreport/golint/README.md b/its/plugin/projects/externalreport/golint/README.md new file mode 100644 index 00000000..983c19e2 --- /dev/null +++ b/its/plugin/projects/externalreport/golint/README.md @@ -0,0 +1,8 @@ +# External Linter Reports + +## Golint + +The file `golint.out` has been generated using the command: +``` +golint > go-lint.out +``` diff --git a/its/plugin/projects/externalreport/golint/SelfAssignement.go b/its/plugin/projects/externalreport/golint/SelfAssignement.go new file mode 100644 index 00000000..377490bf --- /dev/null +++ b/its/plugin/projects/externalreport/golint/SelfAssignement.go @@ -0,0 +1,10 @@ +// S1656 +package samples + +type User struct { name string } + +func (user *User) rename(name string) { + name = name // Noncompliant {{Remove or correct this useless self-assignment.}} + user.name = name // Compliant + user.name = user.name // Noncompliant +} diff --git a/its/plugin/projects/externalreport/golint/TabCharacter.go b/its/plugin/projects/externalreport/golint/TabCharacter.go new file mode 100644 index 00000000..5f7884a0 --- /dev/null +++ b/its/plugin/projects/externalreport/golint/TabCharacter.go @@ -0,0 +1,7 @@ +// S105 +package samples + +func three() int { + x := 3 + return x // Noncompliant +} diff --git a/its/plugin/projects/externalreport/golint/TodoTagPresence.go b/its/plugin/projects/externalreport/golint/TodoTagPresence.go new file mode 100644 index 00000000..245f0ef9 --- /dev/null +++ b/its/plugin/projects/externalreport/golint/TodoTagPresence.go @@ -0,0 +1,8 @@ +// S1135 +package samples + +func todo() { + // Noncompliant@+1 + // Noncompliant@+1 {{Complete the task associated to this TODO comment.}} + // TODO x := 3 +} diff --git a/its/plugin/projects/externalreport/golint/TooLongLine.go b/its/plugin/projects/externalreport/golint/TooLongLine.go new file mode 100644 index 00000000..34c1155b --- /dev/null +++ b/its/plugin/projects/externalreport/golint/TooLongLine.go @@ -0,0 +1,8 @@ +// S103 +package samples + +func longLine() int { + x := 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + // Noncompliant@+1 {{Split this 122 characters long line (which is greater than 120 authorized).}} + return x + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +} diff --git a/its/plugin/projects/externalreport/golint/TooManyParameters.go b/its/plugin/projects/externalreport/golint/TooManyParameters.go new file mode 100644 index 00000000..8d323ba3 --- /dev/null +++ b/its/plugin/projects/externalreport/golint/TooManyParameters.go @@ -0,0 +1,8 @@ +// S107 +package samples + +func functionWith8(p1 int, p2 int, p3 int, p4 int, p5 int, p6 int, p7 int, p8 int) { // Noncompliant {{Function has 8 parameters, which is greater than 7 authorized.}} +} + +func functionWith7(p1 int, p2 int, p3 int, p4 int, p5 int, p6 int, p7 int) { +} diff --git a/its/plugin/projects/externalreport/golint/golint.out b/its/plugin/projects/externalreport/golint/golint.out new file mode 100644 index 00000000..5e4c527d --- /dev/null +++ b/its/plugin/projects/externalreport/golint/golint.out @@ -0,0 +1,11 @@ +SelfAssignement.go:1:1: package comment should be of the form "Package samples ..." +SelfAssignement.go:4:6: exported type User should have comment or be unexported +TabCharacter.go:1:1: package comment should be of the form "Package samples ..." +TodoTagPresence.go:1:1: package comment should be of the form "Package samples ..." +TooLongLine.go:1:1: package comment should be of the form "Package samples ..." +TooManyParameters.go:1:1: package comment should be of the form "Package samples ..." +pivot.go:10:5: don't use underscores in Go names; var ascii_uppercase should be asciiUppercase +pivot.go:11:5: don't use underscores in Go names; var ascii_lowercase should be asciiLowercase +pivot.go:12:5: don't use underscores in Go names; var ascii_uppercase_len should be asciiUppercaseLen +pivot.go:13:5: don't use underscores in Go names; var ascii_lowercase_len should be asciiLowercaseLen +pivot.go:14:5: don't use underscores in Go names; var ascii_allowed should be asciiAllowed diff --git a/its/plugin/projects/externalreport/golint/pivot.go b/its/plugin/projects/externalreport/golint/pivot.go new file mode 100644 index 00000000..6942c337 --- /dev/null +++ b/its/plugin/projects/externalreport/golint/pivot.go @@ -0,0 +1,47 @@ +package samples + +import ( + "io" + "os" + "strings" + "bytes" +) + +var ascii_uppercase = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +var ascii_lowercase = []byte("abcdefghijklmnopqrstuvwxyz") +var ascii_uppercase_len = len(ascii_uppercase) +var ascii_lowercase_len = len(ascii_lowercase) +var ascii_allowed = []byte(" !") + +type rot13Reader struct { + r io.Reader +} + +func rot13(b byte) byte { + pos := bytes.IndexByte(ascii_uppercase, b) + if pos != -1 { + return ascii_uppercase[(pos+13)%ascii_uppercase_len] + } + pos = bytes.IndexByte(ascii_lowercase, b) + if pos != -1 { + return ascii_lowercase[(pos+13)%ascii_lowercase_len] + } + if bytes.IndexByte(ascii_allowed, b) == -1 { + panic("Invalid character") + } + return b +} + +func (r *rot13Reader) Read(b []byte) (n int, err error) { + n, err = r.r.Read(b) + for i := 0; i < n; i++ { + b[i] = rot13(b[i]) + } + return +} + +func main() { + s := strings.NewReader("Lbh penpxrq gur pbqr!") + r := rot13Reader{s} + io.Copy(os.Stdout, &r) +} diff --git a/its/plugin/projects/externalreport/gometalinter/README.md b/its/plugin/projects/externalreport/gometalinter/README.md new file mode 100644 index 00000000..b293771b --- /dev/null +++ b/its/plugin/projects/externalreport/gometalinter/README.md @@ -0,0 +1,8 @@ +# External Linter Reports + +## GoMetaLinter + +The file `gometalinter.out` has been generated using the command: +``` +gometalinter SelfAssignement.go > gometalinter.out +``` diff --git a/its/plugin/projects/externalreport/gometalinter/SelfAssignement.go b/its/plugin/projects/externalreport/gometalinter/SelfAssignement.go new file mode 100644 index 00000000..377490bf --- /dev/null +++ b/its/plugin/projects/externalreport/gometalinter/SelfAssignement.go @@ -0,0 +1,10 @@ +// S1656 +package samples + +type User struct { name string } + +func (user *User) rename(name string) { + name = name // Noncompliant {{Remove or correct this useless self-assignment.}} + user.name = name // Compliant + user.name = user.name // Noncompliant +} diff --git a/its/plugin/projects/externalreport/gometalinter/gometalinter.out b/its/plugin/projects/externalreport/gometalinter/gometalinter.out new file mode 100644 index 00000000..18ae241e --- /dev/null +++ b/its/plugin/projects/externalreport/gometalinter/gometalinter.out @@ -0,0 +1,8 @@ +SelfAssignement.go:1:1:warning: package comment should be of the form "Package samples ..." (golint) +SelfAssignement.go:4:6:warning: exported type User should have comment or be unexported (golint) +SelfAssignement.go:7:3:warning: self-assignment of name to name (SA4018) (megacheck) +SelfAssignement.go:9:3:warning: self-assignment of user.name to user.name (SA4018) (megacheck) +SelfAssignement.go:4:20:warning: field name is unused (U1000) (megacheck) +SelfAssignement.go:6:19:warning: func (*User).rename is unused (U1000) (megacheck) +SelfAssignement.go:7::error: self-assignment of name to name (vet) +SelfAssignement.go:9::error: self-assignment of user.name to user.name (vet) diff --git a/its/plugin/projects/externalreport/govet/README.md b/its/plugin/projects/externalreport/govet/README.md new file mode 100644 index 00000000..e5ddaeb2 --- /dev/null +++ b/its/plugin/projects/externalreport/govet/README.md @@ -0,0 +1,8 @@ +# External Linter Reports + +## go vet + +The file `go-vet.out` has been generated using the command: +``` +go vet 2> go-vet.out || [ $? == 1 ] +``` diff --git a/its/plugin/projects/externalreport/govet/SelfAssignement.go b/its/plugin/projects/externalreport/govet/SelfAssignement.go new file mode 100644 index 00000000..377490bf --- /dev/null +++ b/its/plugin/projects/externalreport/govet/SelfAssignement.go @@ -0,0 +1,10 @@ +// S1656 +package samples + +type User struct { name string } + +func (user *User) rename(name string) { + name = name // Noncompliant {{Remove or correct this useless self-assignment.}} + user.name = name // Compliant + user.name = user.name // Noncompliant +} diff --git a/its/plugin/projects/externalreport/govet/go-vet.out b/its/plugin/projects/externalreport/govet/go-vet.out new file mode 100644 index 00000000..6ecbe134 --- /dev/null +++ b/its/plugin/projects/externalreport/govet/go-vet.out @@ -0,0 +1,3 @@ +SelfAssignement.go:7: self-assignment of name to name +SelfAssignement.go:9: self-assignment of user.name to user.name +exit status 1 diff --git a/its/plugin/projects/measures/go/NoHardcodedCredentialsCheck.go b/its/plugin/projects/measures/go/NoHardcodedCredentialsCheck.go new file mode 100644 index 00000000..4b4dbc78 --- /dev/null +++ b/its/plugin/projects/measures/go/NoHardcodedCredentialsCheck.go @@ -0,0 +1,6 @@ +package samples + +func noHardcodedCredentials() string { + password := "bar" + return password +} diff --git a/its/plugin/projects/measures/go/NoIdenticalConditions.go b/its/plugin/projects/measures/go/NoIdenticalConditions.go new file mode 100644 index 00000000..f1895f00 --- /dev/null +++ b/its/plugin/projects/measures/go/NoIdenticalConditions.go @@ -0,0 +1,9 @@ +package samples + +func identicalIfConditions(cond bool) { + if cond { + + } else if cond { + + } +} diff --git a/its/plugin/projects/measures/go/README.md b/its/plugin/projects/measures/go/README.md new file mode 100644 index 00000000..404693f9 --- /dev/null +++ b/its/plugin/projects/measures/go/README.md @@ -0,0 +1,12 @@ +# Test Report + +The file `go-test-report.out` has been generated in this context: + +* go version 1.10 +* GOPATH defined so the slang git repository is in $GOPATH/src/github.com/SonarSource/slang + +Using the command: +``` +go1.10 test -json | \ + sed 's|github.com/SonarSource/slang/its/plugin/projects/measures/go|samples|g' > go-test-report.out +``` diff --git a/its/plugin/projects/measures/go/RedundantBooleanLiteral.go b/its/plugin/projects/measures/go/RedundantBooleanLiteral.go new file mode 100644 index 00000000..b077ef67 --- /dev/null +++ b/its/plugin/projects/measures/go/RedundantBooleanLiteral.go @@ -0,0 +1,7 @@ +package samples + +func test(flag bool) { + if flag == true { // Noncompliant + + } +} diff --git a/its/plugin/projects/measures/go/SelfAssignement.go b/its/plugin/projects/measures/go/SelfAssignement.go new file mode 100644 index 00000000..377490bf --- /dev/null +++ b/its/plugin/projects/measures/go/SelfAssignement.go @@ -0,0 +1,10 @@ +// S1656 +package samples + +type User struct { name string } + +func (user *User) rename(name string) { + name = name // Noncompliant {{Remove or correct this useless self-assignment.}} + user.name = name // Compliant + user.name = user.name // Noncompliant +} diff --git a/its/plugin/projects/measures/go/TabCharacter.go b/its/plugin/projects/measures/go/TabCharacter.go new file mode 100644 index 00000000..5f7884a0 --- /dev/null +++ b/its/plugin/projects/measures/go/TabCharacter.go @@ -0,0 +1,7 @@ +// S105 +package samples + +func three() int { + x := 3 + return x // Noncompliant +} diff --git a/its/plugin/projects/measures/go/TodoTagPresence.go b/its/plugin/projects/measures/go/TodoTagPresence.go new file mode 100644 index 00000000..245f0ef9 --- /dev/null +++ b/its/plugin/projects/measures/go/TodoTagPresence.go @@ -0,0 +1,8 @@ +// S1135 +package samples + +func todo() { + // Noncompliant@+1 + // Noncompliant@+1 {{Complete the task associated to this TODO comment.}} + // TODO x := 3 +} diff --git a/its/plugin/projects/measures/go/TooLongLine.go b/its/plugin/projects/measures/go/TooLongLine.go new file mode 100644 index 00000000..34c1155b --- /dev/null +++ b/its/plugin/projects/measures/go/TooLongLine.go @@ -0,0 +1,8 @@ +// S103 +package samples + +func longLine() int { + x := 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + // Noncompliant@+1 {{Split this 122 characters long line (which is greater than 120 authorized).}} + return x + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +} diff --git a/its/plugin/projects/measures/go/TooManyParameters.go b/its/plugin/projects/measures/go/TooManyParameters.go new file mode 100644 index 00000000..8d323ba3 --- /dev/null +++ b/its/plugin/projects/measures/go/TooManyParameters.go @@ -0,0 +1,8 @@ +// S107 +package samples + +func functionWith8(p1 int, p2 int, p3 int, p4 int, p5 int, p6 int, p7 int, p8 int) { // Noncompliant {{Function has 8 parameters, which is greater than 7 authorized.}} +} + +func functionWith7(p1 int, p2 int, p3 int, p4 int, p5 int, p6 int, p7 int) { +} diff --git a/its/plugin/projects/measures/go/coverage.out b/its/plugin/projects/measures/go/coverage.out new file mode 100644 index 00000000..6a8c753e --- /dev/null +++ b/its/plugin/projects/measures/go/coverage.out @@ -0,0 +1,25 @@ +mode: set +NoIdenticalConditions.go:3.40,4.10 1 0 +NoIdenticalConditions.go:4.10,6.3 0 0 +NoIdenticalConditions.go:6.3,6.17 1 0 +NoIdenticalConditions.go:6.17,8.3 0 0 +SelfAssignement.go:6.39,10.2 3 0 +TabCharacter.go:4.18,7.2 2 0 +TooManyParameters.go:4.84,5.2 0 0 +TooManyParameters.go:7.76,8.2 0 0 +NoHardcodedCredentialsCheck.go:3.39,6.2 2 0 +RedundantBooleanLiteral.go:3.22,4.18 1 0 +RedundantBooleanLiteral.go:4.18,6.3 0 0 +TodoTagPresence.go:4.13,8.2 0 0 +TooLongLine.go:4.21,8.2 2 0 +pivot.go:20.25,22.15 2 1 +pivot.go:25.2,26.15 2 1 +pivot.go:29.2,29.45 1 1 +pivot.go:32.2,32.10 1 1 +pivot.go:22.15,24.3 1 1 +pivot.go:26.15,28.3 1 1 +pivot.go:29.45,30.29 1 0 +pivot.go:35.57,37.25 2 1 +pivot.go:40.2,40.8 1 1 +pivot.go:37.25,39.3 1 1 +pivot.go:43.13,47.2 3 0 diff --git a/its/plugin/projects/measures/go/duplication/another.go b/its/plugin/projects/measures/go/duplication/another.go new file mode 100644 index 00000000..2ab1c2fd --- /dev/null +++ b/its/plugin/projects/measures/go/duplication/another.go @@ -0,0 +1,41 @@ +package samples + +import ( + "io" + "os" + "strings" + "bytes" +) + +var ascii_uppercase = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +var ascii_lowercase = []byte("abcdefghijklmnopqrstuvwxyz") +var ascii_uppercase_len = len(ascii_uppercase) +var ascii_lowercase_len = len(ascii_lowercase) +var ascii_allowed = []byte(" !") + +type rot13Reader struct { + r io.Reader +} + +func rot13(b byte) byte { + pos := bytes.IndexByte(ascii_uppercase, b) + if pos != -1 { + return ascii_uppercase[(pos+13)%ascii_uppercase_len] + } + pos = bytes.IndexByte(ascii_lowercase, b) + if pos != -1 { + return ascii_lowercase[(pos+13)%ascii_lowercase_len] + } + if bytes.IndexByte(ascii_allowed, b) == -1 { + panic("Invalid character") + } + return b +} + +func (r *rot13Reader) Read(b []byte) (n int, err error) { + n, err = r.r.Read(b) + for i := 0; i < n; i++ { + b[i] = rot13(b[i]) + } + return +} diff --git a/its/plugin/projects/measures/go/duplication/pivot.go b/its/plugin/projects/measures/go/duplication/pivot.go new file mode 100644 index 00000000..6942c337 --- /dev/null +++ b/its/plugin/projects/measures/go/duplication/pivot.go @@ -0,0 +1,47 @@ +package samples + +import ( + "io" + "os" + "strings" + "bytes" +) + +var ascii_uppercase = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +var ascii_lowercase = []byte("abcdefghijklmnopqrstuvwxyz") +var ascii_uppercase_len = len(ascii_uppercase) +var ascii_lowercase_len = len(ascii_lowercase) +var ascii_allowed = []byte(" !") + +type rot13Reader struct { + r io.Reader +} + +func rot13(b byte) byte { + pos := bytes.IndexByte(ascii_uppercase, b) + if pos != -1 { + return ascii_uppercase[(pos+13)%ascii_uppercase_len] + } + pos = bytes.IndexByte(ascii_lowercase, b) + if pos != -1 { + return ascii_lowercase[(pos+13)%ascii_lowercase_len] + } + if bytes.IndexByte(ascii_allowed, b) == -1 { + panic("Invalid character") + } + return b +} + +func (r *rot13Reader) Read(b []byte) (n int, err error) { + n, err = r.r.Read(b) + for i := 0; i < n; i++ { + b[i] = rot13(b[i]) + } + return +} + +func main() { + s := strings.NewReader("Lbh penpxrq gur pbqr!") + r := rot13Reader{s} + io.Copy(os.Stdout, &r) +} diff --git a/its/plugin/projects/measures/go/go-test-report.out b/its/plugin/projects/measures/go/go-test-report.out new file mode 100644 index 00000000..2de50626 --- /dev/null +++ b/its/plugin/projects/measures/go/go-test-report.out @@ -0,0 +1,34 @@ +{"Time":"2018-06-08T10:21:14.402856787+02:00","Action":"run","Package":"go","Test":"Test_rot13"} +{"Time":"2018-06-08T10:21:14.403027275+02:00","Action":"output","Package":"go","Test":"Test_rot13","Output":"=== RUN Test_rot13\n"} +{"Time":"2018-06-08T10:21:14.403047732+02:00","Action":"output","Package":"go","Test":"Test_rot13","Output":"--- PASS: Test_rot13 (0.00s)\n"} +{"Time":"2018-06-08T10:21:14.403053494+02:00","Action":"pass","Package":"go","Test":"Test_rot13","Elapsed":0} +{"Time":"2018-06-08T10:21:14.403077354+02:00","Action":"run","Package":"go","Test":"Test_I_m_diabolic"} +{"Time":"2018-06-08T10:21:14.403082549+02:00","Action":"output","Package":"go","Test":"Test_I_m_diabolic","Output":"=== RUN Test_I_m_diabolic\n"} +{"Time":"2018-06-08T10:21:14.403087893+02:00","Action":"output","Package":"go","Test":"Test_I_m_diabolic","Output":"--- FAIL: Test_I_m_diabolic (0.00s)\n"} +{"Time":"2018-06-08T10:21:14.403091874+02:00","Action":"output","Package":"go","Test":"Test_I_m_diabolic","Output":"\tpivot_test.go:23: I'm diabolic\n"} +{"Time":"2018-06-08T10:21:14.403096042+02:00","Action":"fail","Package":"go","Test":"Test_I_m_diabolic","Elapsed":0} +{"Time":"2018-06-08T10:21:14.403099545+02:00","Action":"run","Package":"go","Test":"Test_I_m_shy"} +{"Time":"2018-06-08T10:21:14.403102719+02:00","Action":"output","Package":"go","Test":"Test_I_m_shy","Output":"=== RUN Test_I_m_shy\n"} +{"Time":"2018-06-08T10:21:14.403107094+02:00","Action":"output","Package":"go","Test":"Test_I_m_shy","Output":"--- SKIP: Test_I_m_shy (0.00s)\n"} +{"Time":"2018-06-08T10:21:14.403110809+02:00","Action":"output","Package":"go","Test":"Test_I_m_shy","Output":"\tpivot_test.go:27: I'm shy\n"} +{"Time":"2018-06-08T10:21:14.40311465+02:00","Action":"skip","Package":"go","Test":"Test_I_m_shy","Elapsed":0} +{"Time":"2018-06-08T10:21:14.403118193+02:00","Action":"run","Package":"go","Test":"Test_I_m_scared"} +{"Time":"2018-06-08T10:21:14.403121389+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"=== RUN Test_I_m_scared\n"} +{"Time":"2018-06-08T10:21:14.403125638+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"--- FAIL: Test_I_m_scared (0.00s)\n"} +{"Time":"2018-06-08T10:21:14.405088641+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"panic: I'am scared [recovered]\n"} +{"Time":"2018-06-08T10:21:14.405096983+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"\tpanic: I'am scared\n"} +{"Time":"2018-06-08T10:21:14.405100938+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"\n"} +{"Time":"2018-06-08T10:21:14.405104521+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"goroutine 21 [running]:\n"} +{"Time":"2018-06-08T10:21:14.405109505+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"testing.tRunner.func1(0xc4200e43c0)\n"} +{"Time":"2018-06-08T10:21:14.405113454+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"\t/home/alban/.gradle/go/binary/1.10/go/src/testing/testing.go:742 +0x29d\n"} +{"Time":"2018-06-08T10:21:14.405118682+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"panic(0x4ffe60, 0x547bd0)\n"} +{"Time":"2018-06-08T10:21:14.405123134+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"\t/home/alban/.gradle/go/binary/1.10/go/src/runtime/panic.go:505 +0x229\n"} +{"Time":"2018-06-08T10:21:14.405126982+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"samples.Test_I_m_scared(0xc4200e43c0)\n"} +{"Time":"2018-06-08T10:21:14.405133214+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"\t/home/alban/go/src/samples/pivot_test.go:31 +0x39\n"} +{"Time":"2018-06-08T10:21:14.405137671+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"testing.tRunner(0xc4200e43c0, 0x538a40)\n"} +{"Time":"2018-06-08T10:21:14.405141337+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"\t/home/alban/.gradle/go/binary/1.10/go/src/testing/testing.go:777 +0xd0\n"} +{"Time":"2018-06-08T10:21:14.405147869+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"created by testing.(*T).Run\n"} +{"Time":"2018-06-08T10:21:14.405152663+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"\t/home/alban/.gradle/go/binary/1.10/go/src/testing/testing.go:824 +0x2e0\n"} +{"Time":"2018-06-08T10:21:14.40531034+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"exit status 2\n"} +{"Time":"2018-06-08T10:21:14.405326924+02:00","Action":"output","Package":"go","Test":"Test_I_m_scared","Output":"FAIL\tsamples\t0.004s\n"} +{"Time":"2018-06-08T10:21:14.405333012+02:00","Action":"fail","Package":"go","Test":"Test_I_m_scared","Elapsed":0.004} diff --git a/its/plugin/projects/measures/go/pivot.go b/its/plugin/projects/measures/go/pivot.go new file mode 100644 index 00000000..32638bda --- /dev/null +++ b/its/plugin/projects/measures/go/pivot.go @@ -0,0 +1,50 @@ +package samples + +import ( + "io" + "os" + "strings" + "bytes" +) +// comment 1 +var ascii_uppercase = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZ") +var ascii_lowercase = []byte("abcdefghijklmnopqrstuvwxyz") +var ascii_uppercase_len = len(ascii_uppercase) +var ascii_lowercase_len = len(ascii_lowercase) +var ascii_allowed = []byte(" !") + +type rot13Reader struct { + r io.Reader +} + +func rot13(b byte) byte { + pos := bytes.IndexByte(ascii_uppercase, b) + if pos != -1 { + return ascii_uppercase[(pos+13)%ascii_uppercase_len] + } + pos = bytes.IndexByte(ascii_lowercase, b) + if pos != -1 { + return ascii_lowercase[(pos+13)%ascii_lowercase_len] + } + if bytes.IndexByte(ascii_allowed, b) == -1 { + panic("Invalid character") + } + return b +} + +func (r *rot13Reader) Read(b []byte) (n int, err error) { + n, err = r.r.Read(b) + for i := 0; i < n; i++ { + b[i] = rot13(b[i]) + } + return +} + +/** + * comment 2 + */ +func main() { + s := strings.NewReader("Lbh penpxrq gur pbqr!") + r := rot13Reader{s} + io.Copy(os.Stdout, &r) +} diff --git a/its/plugin/projects/measures/go/pivot_test.go b/its/plugin/projects/measures/go/pivot_test.go new file mode 100644 index 00000000..1fe15dd8 --- /dev/null +++ b/its/plugin/projects/measures/go/pivot_test.go @@ -0,0 +1,32 @@ +package samples + +import ( + "testing" + "strings" + "io" + "bytes" +) + +func Test_rot13(t *testing.T) { + s := strings.NewReader("Lbh penpxrq gur pbqr!") + r := rot13Reader{s} + var buf bytes.Buffer + io.Copy(&buf, &r) + + expected := "You cracked the code!" + if actual := buf.String(); actual != expected { + t.Fatalf("got '%v'; expected %v", actual, expected) + } +} + +func Test_I_m_diabolic(t *testing.T) { + t.Fatal("I'm diabolic") +} + +func Test_I_m_shy(t *testing.T) { + t.Skip("I'm shy") +} + +func Test_I_m_scared(t *testing.T) { + panic("I'am scared") +} diff --git a/its/plugin/projects/nosonar/go/file.go b/its/plugin/projects/nosonar/go/file.go new file mode 100644 index 00000000..c846577e --- /dev/null +++ b/its/plugin/projects/nosonar/go/file.go @@ -0,0 +1,8 @@ +package main + +func foo(name string) { + if true { // NOSONAR + } + if true { // raise an issue S1145 + } +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/CoverageTest.java b/its/plugin/src/test/java/org/sonarsource/slang/CoverageTest.java new file mode 100644 index 00000000..7de82173 --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/CoverageTest.java @@ -0,0 +1,54 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import com.sonar.orchestrator.build.SonarScanner; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +public class CoverageTest extends TestBase { + + private static final Path BASE_DIRECTORY = Paths.get("projects", "measures"); + + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder(); + + @Test + public void go_coverage() { + final String projectKey = "goCoverage"; + SonarScanner goScanner = getSonarScanner(projectKey, BASE_DIRECTORY.toString(), "go"); + goScanner.setProperty("sonar.go.coverage.reportPaths", "coverage.out"); + + ORCHESTRATOR.executeBuild(goScanner); + + String componentKey = projectKey + ":pivot.go"; + assertThat(getMeasureAsInt(componentKey, "lines_to_cover")).isEqualTo(16); + assertThat(getMeasureAsInt(componentKey, "uncovered_lines")).isEqualTo(4); + assertThat(getMeasureAsInt(componentKey, "conditions_to_cover")).isNull(); + assertThat(getMeasureAsInt(componentKey, "uncovered_conditions")).isNull(); + } +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/DuplicationsTest.java b/its/plugin/src/test/java/org/sonarsource/slang/DuplicationsTest.java new file mode 100644 index 00000000..714851b2 --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/DuplicationsTest.java @@ -0,0 +1,46 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DuplicationsTest extends TestBase { + private static final String BASE_DIRECTORY = "projects/duplications/"; + + @Test + public void go_duplications() { + final String projectKey = "goDuplications"; + ORCHESTRATOR.executeBuild(getSonarScanner(projectKey, BASE_DIRECTORY, "go")); + + assertThat(getMeasureAsInt(projectKey, "duplicated_lines")).isEqualTo(135); + assertThat(getMeasureAsInt(projectKey, "duplicated_blocks")).isEqualTo(5); + assertThat(getMeasureAsInt(projectKey, "duplicated_files")).isEqualTo(3); + assertThat(getMeasure(projectKey, "duplicated_lines_density").getValue()).isEqualTo("97.8"); + + final String componentKey = projectKey + ":pivot.go"; + assertThat(getMeasureAsInt(componentKey, "duplicated_lines")).isEqualTo(47); + assertThat(getMeasureAsInt(componentKey, "duplicated_blocks")).isEqualTo(2); + assertThat(getMeasureAsInt(componentKey, "duplicated_files")).isEqualTo(1); + assertThat(getMeasure(componentKey, "duplicated_lines_density").getValue()).isEqualTo("97.9"); + } + +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/ExternalReportTest.java b/its/plugin/src/test/java/org/sonarsource/slang/ExternalReportTest.java new file mode 100644 index 00000000..8f233dc5 --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/ExternalReportTest.java @@ -0,0 +1,151 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import com.sonar.orchestrator.build.SonarScanner; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonarqube.ws.Issues.Issue; +import org.sonarqube.ws.client.issues.SearchRequest; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +public class ExternalReportTest extends TestBase { + + private static final String BASE_DIRECTORY = "projects/externalreport/"; + + @Rule + public TemporaryFolder tmpDir = new TemporaryFolder(); + + @Test + public void govet() { + final String projectKey = "govet"; + + SonarScanner sonarScanner = getSonarScanner(projectKey, BASE_DIRECTORY, "govet"); + sonarScanner.setProperty("sonar.go.govet.reportPaths", "go-vet.out"); + ORCHESTRATOR.executeBuild(sonarScanner); + List issues = getExternalIssues(projectKey); + assertThat(issues).hasSize(2); + assertThat(formatIssues(issues)).isEqualTo( + "SelfAssignement.go|external_govet:assign|MAJOR|5min|line:7|self-assignment of name to name\n" + + "SelfAssignement.go|external_govet:assign|MAJOR|5min|line:9|self-assignment of user.name to user.name"); + } + + @Test + public void golint() { + final String projectKey = "golint"; + + SonarScanner sonarScanner = getSonarScanner(projectKey, BASE_DIRECTORY, "golint"); + sonarScanner.setProperty("sonar.go.golint.reportPaths", "golint.out"); + ORCHESTRATOR.executeBuild(sonarScanner); + List issues = getExternalIssues(projectKey); + assertThat(issues).hasSize(11); + assertThat(formatIssues(issues)).isEqualTo( + "SelfAssignement.go|external_golint:Exported|MAJOR|5min|line:4|exported type User should have comment or be unexported\n" + + "SelfAssignement.go|external_golint:PackageComment|MAJOR|5min|line:1|package comment should be of the form \"Package samples ...\"\n" + + "TabCharacter.go|external_golint:PackageComment|MAJOR|5min|line:1|package comment should be of the form \"Package samples ...\"\n" + + "TodoTagPresence.go|external_golint:PackageComment|MAJOR|5min|line:1|package comment should be of the form \"Package samples ...\"\n" + + "TooLongLine.go|external_golint:PackageComment|MAJOR|5min|line:1|package comment should be of the form \"Package samples ...\"\n" + + "TooManyParameters.go|external_golint:PackageComment|MAJOR|5min|line:1|package comment should be of the form \"Package samples ...\"\n" + + "pivot.go|external_golint:Names|MAJOR|5min|line:10|don't use underscores in Go names; var ascii_uppercase should be asciiUppercase\n" + + "pivot.go|external_golint:Names|MAJOR|5min|line:11|don't use underscores in Go names; var ascii_lowercase should be asciiLowercase\n" + + "pivot.go|external_golint:Names|MAJOR|5min|line:12|don't use underscores in Go names; var ascii_uppercase_len should be asciiUppercaseLen\n" + + "pivot.go|external_golint:Names|MAJOR|5min|line:13|don't use underscores in Go names; var ascii_lowercase_len should be asciiLowercaseLen\n" + + "pivot.go|external_golint:Names|MAJOR|5min|line:14|don't use underscores in Go names; var ascii_allowed should be asciiAllowed" + ); + } + + @Test + public void gometalinter() { + final String projectKey = "gometalinter"; + + SonarScanner sonarScanner = getSonarScanner(projectKey, BASE_DIRECTORY, "gometalinter"); + sonarScanner.setProperty("sonar.go.gometalinter.reportPaths", "gometalinter.out"); + ORCHESTRATOR.executeBuild(sonarScanner); + List issues = getExternalIssues(projectKey); + assertThat(issues).hasSize(8); + assertThat(formatIssues(issues)).isEqualTo( + "SelfAssignement.go|external_golint:Exported|MAJOR|5min|line:4|exported type User should have comment or be unexported\n" + + "SelfAssignement.go|external_golint:PackageComment|MAJOR|5min|line:1|package comment should be of the form \"Package samples ...\"\n" + + "SelfAssignement.go|external_govet:assign|MAJOR|5min|line:7|self-assignment of name to name\n" + + "SelfAssignement.go|external_govet:assign|MAJOR|5min|line:9|self-assignment of user.name to user.name\n" + + "SelfAssignement.go|external_megacheck:SA4018|MAJOR|5min|line:7|self-assignment of name to name\n" + + "SelfAssignement.go|external_megacheck:SA4018|MAJOR|5min|line:9|self-assignment of user.name to user.name\n" + + "SelfAssignement.go|external_megacheck:U1000|MAJOR|5min|line:4|field name is unused\n" + + "SelfAssignement.go|external_megacheck:U1000|MAJOR|5min|line:6|func (*User).rename is unused"); + } + + @Test + public void golangcilint() { + final String projectKey = "golangcilint"; + + SonarScanner sonarScanner = getSonarScanner(projectKey, BASE_DIRECTORY, "golangci-lint"); + sonarScanner.setProperty("sonar.go.golangci-lint.reportPaths", "golangci-lint-checkstyle.xml"); + ORCHESTRATOR.executeBuild(sonarScanner); + List issues = getExternalIssues(projectKey); + assertThat(issues).hasSize(4); + assertThat(formatIssues(issues)).isEqualTo( + "SelfAssignement.go|external_golangci-lint:govet.bug.major|MAJOR|5min|line:7|assign: self-assignment of name to name\n" + + "SelfAssignement.go|external_golangci-lint:govet.bug.major|MAJOR|5min|line:9|assign: self-assignment of user.name to user.name\n" + + "SelfAssignement.go|external_golangci-lint:unused.bug.major|MAJOR|5min|line:4|U1000: field `name` is unused\n" + + "SelfAssignement.go|external_golangci-lint:unused.bug.major|MAJOR|5min|line:6|U1000: func `(*User).rename` is unused"); + } + + private List getExternalIssues(String componentKey) { + return newWsClient().issues().search(new SearchRequest().setComponentKeys(Collections.singletonList(componentKey))) + .getIssuesList().stream() + .filter(issue -> issue.getRule().startsWith("external_")) + .collect(Collectors.toList()); + } + + private Path createTemporaryReportFromTemplate(Path sourceReportPath, String placeHolder, String newValue) throws IOException { + String reportContent = new String(Files.readAllBytes(sourceReportPath), UTF_8); + reportContent = reportContent.replace(placeHolder, newValue); + Path destReportPath = tmpDir.newFile(sourceReportPath.getFileName().toString()).toPath().toRealPath(); + Files.write(destReportPath, reportContent.getBytes(UTF_8)); + return destReportPath; + } + + private static String formatIssues(List issues) { + return issues.stream() + .map(issue -> filePath(issue) + "|" + + issue.getRule() + "|" + + issue.getSeverity().name() + "|" + + issue.getDebt() + "|" + + "line:" + issue.getLine() + "|" + + issue.getMessage()) + .sorted() + .collect(Collectors.joining("\n")); + } + + private static String filePath(Issue issue) { + return issue.getComponent().substring(issue.getComponent().indexOf(':') + 1); + } + +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/MeasuresTest.java b/its/plugin/src/test/java/org/sonarsource/slang/MeasuresTest.java new file mode 100644 index 00000000..3876b6fb --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/MeasuresTest.java @@ -0,0 +1,50 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import java.util.List; +import org.junit.Test; +import org.sonarqube.ws.Issues; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MeasuresTest extends TestBase { + + private static final String BASE_DIRECTORY = "projects/measures/"; + + @Test + public void go_measures() { + final String projectKey = "goMeasures"; + ORCHESTRATOR.executeBuild(getSonarScanner(projectKey, BASE_DIRECTORY, "go")); + + final String componentKey = projectKey + ":pivot.go"; + assertThat(getMeasureAsInt(componentKey, "ncloc")).isEqualTo(41); + assertThat(getMeasureAsInt(componentKey, "comment_lines")).isEqualTo(2); + + assertThat(getMeasure(componentKey, "ncloc_data").getValue()) + .isEqualTo("1=1;3=1;4=1;5=1;6=1;7=1;8=1;10=1;11=1;12=1;13=1;14=1;16=1;17=1;18=1;20=1;21=1;22=1;23=1;24=1;25=1;" + + "26=1;27=1;28=1;29=1;30=1;31=1;32=1;33=1;35=1;36=1;37=1;38=1;39=1;40=1;41=1;46=1;47=1;48=1;49=1;50=1"); + + assertThat(getMeasureAsInt(componentKey, "functions")).isEqualTo(3); + + assertThat(getMeasure(componentKey, "executable_lines_data").getValue()) + .isEqualTo("32=1;36=1;37=1;38=1;40=1;49=1;22=1;23=1;25=1;26=1;27=1;29=1;30=1"); + } +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/NoSonarTest.java b/its/plugin/src/test/java/org/sonarsource/slang/NoSonarTest.java new file mode 100644 index 00000000..7b02d198 --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/NoSonarTest.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class NoSonarTest extends TestBase { + + private static final String BASE_DIRECTORY = "projects/nosonar/"; + private static final String NO_SONAR_PROFILE_NAME = "nosonar-profile"; + private static final String RULE_KEY = "S1145"; + + @Test + public void test_go_nosonar() { + String projectKey = "goNoSonar"; + String language = "go"; + ORCHESTRATOR.executeBuild(getSonarScanner(projectKey, BASE_DIRECTORY, language, NO_SONAR_PROFILE_NAME)); + + assertThat(getMeasureAsInt(projectKey, "files")).isEqualTo(1); + assertThat(getIssuesForRule(projectKey, language + ":" + RULE_KEY)).hasSize(1); + } +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/SonarLintTest.java b/its/plugin/src/test/java/org/sonarsource/slang/SonarLintTest.java new file mode 100644 index 00000000..52825ffb --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/SonarLintTest.java @@ -0,0 +1,198 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.OrchestratorBuilder; +import com.sonar.orchestrator.locator.Locators; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.io.FileUtils; +import org.assertj.core.groups.Tuple; +import org.jetbrains.annotations.NotNull; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; +import org.sonarsource.sonarlint.core.StandaloneSonarLintEngineImpl; +import org.sonarsource.sonarlint.core.analysis.api.ClientInputFile; +import org.sonarsource.sonarlint.core.client.api.common.analysis.Issue; +import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneAnalysisConfiguration; +import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneGlobalConfiguration; +import org.sonarsource.sonarlint.core.client.api.standalone.StandaloneSonarLintEngine; +import org.sonarsource.sonarlint.core.commons.IssueSeverity; +import org.sonarsource.sonarlint.core.commons.Language; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.tuple; + +public class SonarLintTest { + + @ClassRule + public static TemporaryFolder temp = new TemporaryFolder(); + + private static StandaloneSonarLintEngine sonarlintEngine; + + private static File baseDir; + + @BeforeClass + public static void prepare() throws Exception { + // Orchestrator is used only to retrieve plugin artifacts from filesystem or maven + OrchestratorBuilder orchestratorBuilder = Orchestrator.builderEnv(); + Tests.addGoPlugin(orchestratorBuilder); + Orchestrator orchestrator = orchestratorBuilder + .useDefaultAdminCredentialsForBuilds(true) + .setSonarVersion(System.getProperty(Tests.SQ_VERSION_PROPERTY, Tests.DEFAULT_SQ_VERSION)) + .build(); + + Locators locators = orchestrator.getConfiguration().locators(); + StandaloneGlobalConfiguration.Builder sonarLintConfigBuilder = StandaloneGlobalConfiguration.builder(); + orchestrator.getDistribution().getPluginLocations().stream() + .filter(location -> !location.toString().contains("sonar-reset-data-plugin")) + .map(plugin -> locators.locate(plugin).toPath()) + .forEach(sonarLintConfigBuilder::addPlugin); + + sonarLintConfigBuilder + .setSonarLintUserHome(temp.newFolder().toPath()) + .setLogOutput((formattedMessage, level) -> { + /* Don't pollute logs */ + }); + StandaloneGlobalConfiguration configuration = sonarLintConfigBuilder + .addEnabledLanguage(Language.GO) + .build(); + sonarlintEngine = new StandaloneSonarLintEngineImpl(configuration); + baseDir = temp.newFolder(); + } + + @AfterClass + public static void stop() { + sonarlintEngine.stop(); + } + + @Test + public void test_go() throws Exception { + ClientInputFile inputFile = prepareInputFile( + "package main\n" + + "func empty() {\n" // go:S1186 (empty function) + + "}\n" + ); + + assertIssues(analyzeWithSonarLint(inputFile), + tuple("go:S1186", 2, inputFile.getPath(), IssueSeverity.CRITICAL)); + } + + @Test + public void test_go_nosonar() throws Exception { + ClientInputFile goInputFile = prepareInputFile( + "package main\n" + + "func empty() { // NOSONAR\n" // skipped go:S1186 (empty function) + + "}\n" + ); + assertThat(analyzeWithSonarLint(goInputFile)).isEmpty(); + } + + private List analyzeWithSonarLint(ClientInputFile inputFile) { + List issues = new ArrayList<>(); + StandaloneAnalysisConfiguration analysisConfiguration = StandaloneAnalysisConfiguration.builder() + .setBaseDir(baseDir.toPath()) + .addInputFiles(Collections.singletonList(inputFile)) + .build(); + + sonarlintEngine.analyze(analysisConfiguration, issues::add, null, null); + + return issues; + } + + private void assertIssues(List issues, Tuple... expectedIssues) { + assertThat(issues) + .extracting(Issue::getRuleKey, Issue::getStartLine, issue -> issue.getInputFile().getPath(), Issue::getSeverity) + .containsExactlyInAnyOrder(expectedIssues); + } + + private ClientInputFile prepareInputFile(String content) throws IOException { + File file = new File(baseDir, "foo.go"); + FileUtils.write(file, content, StandardCharsets.UTF_8); + return createInputFile(file.toPath()); + } + + private ClientInputFile createInputFile(final Path path) { + return new ClientInputFile() { + + @Override + public URI uri() { + return path.toUri(); + } + + @Override + public String getPath() { + return path.toString(); + } + + @Override + public boolean isTest() { + return false; + } + + @Override + public Charset getCharset() { + return StandardCharsets.UTF_8; + } + + + @Override + public G getClientObject() { + return null; + } + + @Override + public String contents() throws IOException { + return Files.readString(path); + } + + @Override + public String relativePath() { + return path.toString(); + } + + @Override + public InputStream inputStream() throws IOException { + return Files.newInputStream(path); + } + + @NotNull + @Override + public Language language() { + return Language.forKey("go").get(); + } + }; + } + +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/TestBase.java b/its/plugin/src/test/java/org/sonarsource/slang/TestBase.java new file mode 100644 index 00000000..bc6731fb --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/TestBase.java @@ -0,0 +1,101 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.build.SonarScanner; +import java.io.File; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.junit.ClassRule; +import org.sonarqube.ws.Issues; +import org.sonarqube.ws.Measures.ComponentWsResponse; +import org.sonarqube.ws.Measures.Measure; +import org.sonarqube.ws.client.HttpConnector; +import org.sonarqube.ws.client.WsClient; +import org.sonarqube.ws.client.WsClientFactories; +import org.sonarqube.ws.client.issues.SearchRequest; +import org.sonarqube.ws.client.measures.ComponentRequest; + +import static java.util.Collections.singletonList; + +public abstract class TestBase { + + @ClassRule + public static final Orchestrator ORCHESTRATOR = Tests.ORCHESTRATOR; + + protected SonarScanner getSonarScanner(String projectKey, String directoryToScan, String languageKey) { + return getSonarScanner(projectKey, directoryToScan, languageKey, null); + } + + protected SonarScanner getSonarScanner(String projectKey, String directoryToScan, String languageKey, @Nullable String profileName) { + ORCHESTRATOR.getServer().provisionProject(projectKey, projectKey); + if (profileName != null) { + ORCHESTRATOR.getServer().associateProjectToQualityProfile(projectKey, languageKey, profileName); + } + return SonarScanner.create() + .setProjectDir(new File(directoryToScan, languageKey)) + .setProjectKey(projectKey) + .setProjectName(projectKey) + .setProjectVersion("1") + .setSourceDirs("."); + } + + protected Measure getMeasure(String projectKey, String metricKey) { + return getMeasure(projectKey, null, metricKey); + } + + protected Measure getMeasure(String projectKey, @Nullable String componentKey, String metricKey) { + String component; + if (componentKey != null) { + component = projectKey + ":" + componentKey; + } else { + component = projectKey; + } + ComponentWsResponse response = newWsClient().measures().component(new ComponentRequest() + .setComponent(component) + .setMetricKeys(singletonList(metricKey))); + List measures = response.getComponent().getMeasuresList(); + return measures.size() == 1 ? measures.get(0) : null; + } + + protected List getIssuesForRule(String componentKey, String rule) { + return newWsClient().issues().search(new SearchRequest() + .setRules(Collections.singletonList(rule)) + .setComponentKeys(Collections.singletonList(componentKey))).getIssuesList(); + } + + protected Integer getMeasureAsInt(String componentKey, String metricKey) { + Measure measure = getMeasure(componentKey, metricKey); + return (measure == null) ? null : Integer.parseInt(measure.getValue()); + } + + protected static WsClient newWsClient() { + return WsClientFactories.getDefault().newClient(HttpConnector.newBuilder() + .url(ORCHESTRATOR.getServer().getUrl()) + .build()); + } + +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/TestReportTest.java b/its/plugin/src/test/java/org/sonarsource/slang/TestReportTest.java new file mode 100644 index 00000000..bfafca2e --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/TestReportTest.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.build.SonarScanner; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.ClassRule; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TestReportTest extends TestBase { + + private static final Path BASE_DIRECTORY = Paths.get("projects","measures"); + + @ClassRule + public static Orchestrator orchestrator = Tests.ORCHESTRATOR; + + @Test + public void go_test_report() { + final String projectKey = "goTestReport"; + SonarScanner goScanner = getSonarScanner(projectKey, BASE_DIRECTORY.toString(), "go"); + goScanner.setProperty("sonar.tests", "."); + goScanner.setProperty("sonar.test.inclusions", "**/*_test.go"); + goScanner.setProperty("sonar.go.tests.reportPaths", "go-test-report.out"); + + ORCHESTRATOR.executeBuild(goScanner); + + assertThat(getMeasureAsInt(projectKey, "tests")).isEqualTo(4); + assertThat(getMeasureAsInt(projectKey, "test_failures")).isEqualTo(2); + assertThat(getMeasureAsInt(projectKey, "test_errors")).isNull(); + assertThat(getMeasureAsInt(projectKey, "skipped_tests")).isEqualTo(1); + assertThat(getMeasureAsInt(projectKey, "test_execution_time")).isEqualTo(4); + + final String componentKey = projectKey + ":pivot_test.go"; + assertThat(getMeasureAsInt(componentKey, "tests")).isEqualTo(4); + assertThat(getMeasureAsInt(componentKey, "test_failures")).isEqualTo(2); + assertThat(getMeasureAsInt(componentKey, "test_errors")).isEqualTo(0); + assertThat(getMeasureAsInt(componentKey, "skipped_tests")).isEqualTo(1); + assertThat(getMeasureAsInt(componentKey, "test_execution_time")).isEqualTo(4); + } + +} diff --git a/its/plugin/src/test/java/org/sonarsource/slang/Tests.java b/its/plugin/src/test/java/org/sonarsource/slang/Tests.java new file mode 100644 index 00000000..66ae05ee --- /dev/null +++ b/its/plugin/src/test/java/org/sonarsource/slang/Tests.java @@ -0,0 +1,77 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.OrchestratorBuilder; +import com.sonar.orchestrator.locator.FileLocation; +import com.sonar.orchestrator.locator.Location; +import com.sonar.orchestrator.locator.MavenLocation; +import java.io.File; + +import org.apache.commons.lang.StringUtils; +import org.junit.ClassRule; +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + CoverageTest.class, + TestReportTest.class, + DuplicationsTest.class, + ExternalReportTest.class, + MeasuresTest.class, + NoSonarTest.class, +}) +public class Tests { + + static final String SQ_VERSION_PROPERTY = "sonar.runtimeVersion"; + static final String DEFAULT_SQ_VERSION = "LATEST_RELEASE"; + + @ClassRule + public static final Orchestrator ORCHESTRATOR; + + static { + OrchestratorBuilder orchestratorBuilder = Orchestrator.builderEnv(); + addGoPlugin(orchestratorBuilder); + ORCHESTRATOR = orchestratorBuilder + .useDefaultAdminCredentialsForBuilds(true) + .setSonarVersion(System.getProperty(SQ_VERSION_PROPERTY, DEFAULT_SQ_VERSION)) + .restoreProfileAtStartup(FileLocation.of("src/test/resources/nosonar-go.xml")) + .build(); + } + + static void addGoPlugin(OrchestratorBuilder builder) { + String plugin = "sonar-go-plugin"; + String slangVersion = System.getProperty("slangVersion"); + + Location pluginLocation; + if (StringUtils.isEmpty(slangVersion)) { + // use the plugin that was built on local machine + pluginLocation = FileLocation.byWildcardMavenFilename(new File("../../" + plugin + "/build/libs"), plugin + "-*-all.jar"); + } else { + // QA environment downloads the plugin built by the CI job + pluginLocation = MavenLocation.of("org.sonarsource.slang", plugin, slangVersion); + } + + builder.addPlugin(pluginLocation); + } + +} diff --git a/its/plugin/src/test/resources/nosonar-go.xml b/its/plugin/src/test/resources/nosonar-go.xml new file mode 100644 index 00000000..22bde1dc --- /dev/null +++ b/its/plugin/src/test/resources/nosonar-go.xml @@ -0,0 +1,12 @@ + + + nosonar-profile + go + + + go + S1145 + INFO + + + diff --git a/its/ruling/build.gradle b/its/ruling/build.gradle new file mode 100644 index 00000000..7eb117e7 --- /dev/null +++ b/its/ruling/build.gradle @@ -0,0 +1,17 @@ +dependencies { + testImplementation 'org.sonarsource.orchestrator:sonar-orchestrator' + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.sonarsource.analyzer-commons:sonar-analyzer-commons' +} + +sonarqube.skipProject = true + +test { + onlyIf { + project.hasProperty("its") || + project.hasProperty("ruling") + } + + systemProperty 'java.awt.headless', 'true' + outputs.upToDateWhen { false } +} diff --git a/its/ruling/src/test/java/org/sonarsource/slang/SlangRulingTest.java b/its/ruling/src/test/java/org/sonarsource/slang/SlangRulingTest.java new file mode 100644 index 00000000..1173a15f --- /dev/null +++ b/its/ruling/src/test/java/org/sonarsource/slang/SlangRulingTest.java @@ -0,0 +1,152 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang; + +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.OrchestratorBuilder; +import com.sonar.orchestrator.build.SonarScanner; +import com.sonar.orchestrator.locator.FileLocation; +import com.sonar.orchestrator.locator.Location; +import com.sonar.orchestrator.locator.MavenLocation; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; + +import org.apache.commons.lang.StringUtils; +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.sonarsource.analyzer.commons.ProfileGenerator; + +import static org.assertj.core.api.Assertions.assertThat; + +public class SlangRulingTest { + + private static final String SQ_VERSION_PROPERTY = "sonar.runtimeVersion"; + private static final String DEFAULT_SQ_VERSION = "LATEST_RELEASE"; + + private static Orchestrator orchestrator; + private static boolean keepSonarqubeRunning = "true".equals(System.getProperty("keepSonarqubeRunning")); + + + @BeforeClass + public static void setUp() { + OrchestratorBuilder builder = Orchestrator.builderEnv() + .useDefaultAdminCredentialsForBuilds(true) + .setSonarVersion(System.getProperty(SQ_VERSION_PROPERTY, DEFAULT_SQ_VERSION)) + .addPlugin(MavenLocation.of("org.sonarsource.sonar-lits-plugin", "sonar-lits-plugin", "0.10.0.2181")); + + addGoPlugin(builder); + + orchestrator = builder.build(); + orchestrator.start(); + + ProfileGenerator.RulesConfiguration goRulesConfiguration = new ProfileGenerator.RulesConfiguration(); + goRulesConfiguration.add("S1451", "headerFormat", "^(?i).*copyright"); + goRulesConfiguration.add("S1451", "isRegularExpression", "true"); + + File goProfile = ProfileGenerator.generateProfile(SlangRulingTest.orchestrator.getServer().getUrl(), "go", "go", goRulesConfiguration, Collections.emptySet()); + + orchestrator.getServer().restoreProfile(FileLocation.of(goProfile)); + } + + static void addGoPlugin(OrchestratorBuilder builder) { + String plugin = "sonar-go-plugin"; + String slangVersion = System.getProperty("slangVersion"); + + Location pluginLocation; + if (StringUtils.isEmpty(slangVersion)) { + // use the plugin that was built on local machine + pluginLocation = FileLocation.byWildcardMavenFilename(new File("../../" + plugin + "/build/libs"), plugin + "-*-all.jar"); + } else { + // QA environment downloads the plugin built by the CI job + pluginLocation = MavenLocation.of("org.sonarsource.slang", plugin, slangVersion); + } + + builder.addPlugin(pluginLocation); + } + + @Test + // @Ignore because it should only be run manually + @Ignore + public void go_manual_keep_sonarqube_server_up() throws IOException { + keepSonarqubeRunning = true; + test_go(); + } + + + @Test + public void test_go() throws IOException { + Map properties = new HashMap<>(); + properties.put("sonar.inclusions", "sources/go/**/*.go, ruling/src/test/resources/sources/go/**/*.go"); + properties.put("sonar.exclusions", "**/*generated*.go, **/*.pb.go"); + properties.put("sonar.tests", "."); + properties.put("sonar.test.inclusions", "**/*_test.go"); + run_ruling_test(properties); + } + + private void run_ruling_test(Map projectProperties) throws IOException { + Map properties = new HashMap<>(projectProperties); + properties.put("sonar.slang.converter.validation", "log"); + properties.put("sonar.slang.duration.statistics", "true"); + + String projectKey = "go".replace("/", "-") + "-project"; + orchestrator.getServer().provisionProject(projectKey, projectKey); + orchestrator.getServer().associateProjectToQualityProfile(projectKey, "go", "rules"); + + File actualDirectory = FileLocation.of("build/tmp/actual/" + "go").getFile(); + actualDirectory.mkdirs(); + + File litsDifferencesFile = FileLocation.of("build/" + projectKey + "-differences").getFile(); + SonarScanner build = SonarScanner.create(FileLocation.of("../").getFile()) + .setProjectKey(projectKey) + .setProjectName(projectKey) + .setProjectVersion("1") + .setSourceDirs("./") + .setSourceEncoding("utf-8") + .setProperties(properties) + .setProperty("sonar.lits.dump.old", FileLocation.of("src/test/resources/expected/" + "go").getFile().getAbsolutePath()) + .setProperty("sonar.lits.dump.new", actualDirectory.getAbsolutePath()) + .setProperty("sonar.lits.differences", litsDifferencesFile.getAbsolutePath()) + .setProperty("sonar.cpd.exclusions", "**/*") + .setProperty("sonar.scm.disabled", "true") + .setProperty("sonar.project", "go") + .setEnvironmentVariable("SONAR_RUNNER_OPTS", "-Xmx1024m"); + + orchestrator.executeBuild(build); + + String litsDifference = new String(Files.readAllBytes(litsDifferencesFile.toPath())); + assertThat(litsDifference).isEmpty(); + } + + @AfterClass + public static void after() { + if (keepSonarqubeRunning) { + // keep server running, use CTRL-C to stop it + new Scanner(System.in).next(); + } + } + +} diff --git a/its/ruling/src/test/resources/expected/go/go-ParsingError.json b/its/ruling/src/test/resources/expected/go/go-ParsingError.json new file mode 100644 index 00000000..ce947cae --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-ParsingError.json @@ -0,0 +1,8 @@ +{ +'go-project:ruling/src/test/resources/sources/go/ParsingError.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/routes/data/swagger/datafile.go':[ +0, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S100.json b/its/ruling/src/test/resources/expected/go/go-S100.json new file mode 100644 index 00000000..c23cc84e --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S100.json @@ -0,0 +1,442 @@ +{ +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go':[ +42, +76, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +261, +272, +285, +294, +312, +327, +336, +351, +368, +396, +433, +462, +513, +532, +555, +592, +658, +676, +692, +709, +], +'go-project:sources/go/kubernetes/pkg/api/v1/defaults.go':[ +30, +41, +45, +49, +68, +75, +80, +99, +126, +156, +178, +192, +198, +204, +210, +215, +221, +229, +234, +239, +257, +268, +276, +281, +286, +295, +300, +331, +348, +360, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/v1alpha1/defaults.go':[ +27, +34, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/conversion.go':[ +87, +128, +168, +179, +191, +210, +237, +264, +292, +305, +318, +328, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/defaults.go':[ +29, +71, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/conversion.go':[ +45, +116, +191, +212, +236, +255, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/defaults.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v2alpha1/defaults.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/v1/conversion.go':[ +50, +68, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/v1/defaults.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/v2alpha1/defaults.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/apis/certificates/v1beta1/defaults.go':[ +24, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/defaults.go':[ +56, +139, +178, +194, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/conversion.go':[ +85, +104, +131, +159, +186, +199, +212, +228, +238, +248, +255, +266, +278, +283, +288, +301, +314, +332, +350, +370, +390, +401, +412, +423, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/defaults.go':[ +30, +65, +105, +125, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/v1/conversion.go':[ +45, +50, +55, +68, +81, +99, +117, +137, +157, +168, +179, +190, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/v1/defaults.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1alpha1/conversion.go':[ +29, +60, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1alpha1/defaults.go':[ +27, +32, +37, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1beta1/defaults.go':[ +27, +32, +37, +], +'go-project:sources/go/kubernetes/pkg/generated/bindata.go':[ +211, +215, +3671, +3675, +3688, +3692, +7147, +7151, +7164, +7168, +7277, +7281, +7294, +7298, +7403, +7407, +9555, +9559, +9661, +9665, +9678, +9682, +9784, +9788, +9801, +9805, +9846, +9850, +9863, +9867, +9908, +9912, +10138, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1/defaults.go':[ +25, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/node/graph.go':[ +121, +129, +135, +156, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go':[ +32, +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/conversion.go':[ +27, +41, +55, +66, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go':[ +74, +83, +89, +98, +104, +113, +119, +128, +138, +147, +158, +166, +172, +178, +185, +193, +202, +211, +219, +228, +233, +244, +253, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/watch.go':[ +40, +53, +57, +71, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +125, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/conversion.go':[ +49, +57, +73, +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/embedded.go':[ +91, +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +261, +272, +285, +294, +312, +327, +336, +351, +368, +396, +433, +462, +513, +532, +555, +592, +658, +676, +692, +709, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/defaults.go':[ +30, +41, +45, +49, +68, +75, +80, +99, +126, +156, +178, +192, +198, +204, +210, +215, +221, +229, +234, +239, +257, +268, +276, +281, +286, +295, +300, +331, +348, +360, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/admissionregistration/v1alpha1/defaults.go':[ +27, +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go':[ +87, +128, +168, +179, +191, +210, +237, +264, +292, +305, +318, +328, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/defaults.go':[ +29, +71, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/conversion.go':[ +45, +116, +191, +212, +236, +255, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/defaults.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v2alpha1/defaults.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/v1/conversion.go':[ +50, +68, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/v1/defaults.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/v2alpha1/defaults.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/certificates/v1beta1/defaults.go':[ +24, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/conversion.go':[ +85, +104, +131, +159, +186, +199, +212, +228, +238, +248, +255, +266, +278, +283, +288, +301, +314, +332, +350, +370, +390, +401, +412, +423, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/defaults.go':[ +30, +65, +105, +125, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/networking/v1/conversion.go':[ +45, +50, +55, +68, +81, +99, +117, +137, +157, +168, +179, +190, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/networking/v1/defaults.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1alpha1/conversion.go':[ +29, +60, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1alpha1/defaults.go':[ +27, +32, +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1beta1/defaults.go':[ +27, +32, +37, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S103.json b/its/ruling/src/test/resources/expected/go/go-S103.json new file mode 100644 index 00000000..7f23fbcc --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S103.json @@ -0,0 +1,13854 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S103.go':[ +7, +], +'go-project:ruling/src/test/resources/sources/go/S107.go':[ +4, +], +'go-project:sources/go/kubernetes/cluster/gce/gci/mounter/mounter.go':[ +32, +76, +], +'go-project:sources/go/kubernetes/cluster/images/etcd-version-monitor/etcd-version-monitor.go':[ +44, +45, +], +'go-project:sources/go/kubernetes/cmd/cloud-controller-manager/app/controllermanager.go':[ +132, +197, +244, +246, +251, +], +'go-project:sources/go/kubernetes/cmd/cloud-controller-manager/app/options/options.go':[ +71, +73, +74, +77, +78, +79, +80, +81, +83, +85, +86, +87, +91, +], +'go-project:sources/go/kubernetes/cmd/genman/gen_kube_man.go':[ +197, +], +'go-project:sources/go/kubernetes/cmd/gke-certificates-controller/app/gke_certificates_controller.go':[ +67, +], +'go-project:sources/go/kubernetes/cmd/gke-certificates-controller/app/gke_signer.go':[ +52, +], +'go-project:sources/go/kubernetes/cmd/gke-certificates-controller/app/options.go':[ +48, +50, +51, +53, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/hyperkube.go':[ +77, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/kube-aggregator.go':[ +35, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/kube-apiserver.go':[ +34, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/kube-controller-manager.go':[ +33, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/kube-scheduler.go':[ +33, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/aggregator.go':[ +43, +51, +53, +91, +102, +109, +152, +194, +195, +196, +197, +225, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/apiextensions.go':[ +31, +33, +51, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/options/options.go':[ +79, +189, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/options/validation.go':[ +39, +43, +70, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +119, +135, +144, +158, +164, +171, +181, +186, +240, +254, +259, +335, +395, +421, +456, +474, +495, +498, +527, +548, +596, +752, +753, +763, +765, +771, +809, +821, +823, +831, +834, +840, +844, +852, +855, +872, +873, +892, +894, +901, +905, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/autoscaling.go':[ +32, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/certificates.go':[ +32, +57, +67, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/controllermanager.go':[ +157, +179, +341, +392, +458, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/core.go':[ +120, +123, +126, +131, +158, +237, +238, +295, +302, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/extensions.go':[ +31, +45, +58, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/options/options.go':[ +133, +134, +135, +136, +137, +138, +139, +140, +141, +142, +143, +144, +146, +147, +149, +150, +152, +153, +154, +156, +159, +160, +161, +162, +163, +164, +165, +166, +167, +168, +169, +170, +171, +172, +173, +174, +175, +176, +177, +178, +179, +181, +194, +195, +196, +197, +202, +211, +212, +213, +214, +218, +219, +220, +221, +222, +223, +224, +225, +226, +227, +228, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/plugins.go':[ +86, +104, +105, +114, +115, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/server.go':[ +124, +125, +129, +130, +131, +132, +133, +134, +135, +137, +138, +139, +140, +141, +142, +143, +144, +145, +146, +147, +148, +149, +150, +152, +157, +158, +163, +190, +305, +386, +420, +715, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/install/install.go':[ +33, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation/validation.go':[ +72, +218, +220, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/defaults.go':[ +36, +57, +69, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/init.go':[ +91, +100, +112, +135, +137, +142, +173, +226, +236, +262, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/phases/certs.go':[ +55, +67, +68, +69, +70, +71, +98, +99, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/phases/kubeconfig.go':[ +72, +77, +78, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/reset.go':[ +89, +99, +110, +121, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/token.go':[ +115, +123, +125, +159, +196, +260, +320, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/constants/constants.go':[ +76, +112, +115, +118, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/discovery/discovery.go':[ +52, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/discovery/file/file.go':[ +75, +78, +80, +98, +103, +115, +127, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/discovery/token/token.go':[ +58, +63, +73, +77, +85, +88, +95, +105, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/images/images.go':[ +43, +44, +45, +46, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/master/manifests.go':[ +49, +50, +102, +335, +336, +351, +352, +413, +424, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/master/selfhosted.go':[ +75, +124, +149, +196, +240, +275, +292, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/node/csr.go':[ +38, +45, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/node/validate.go':[ +43, +49, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/addons/addons.go':[ +48, +57, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/apiconfig/clusterroles.go':[ +38, +49, +93, +153, +254, +256, +260, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/certs.go':[ +113, +120, +139, +165, +182, +200, +230, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers.go':[ +30, +50, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/kubeconfig/doc.go':[ +25, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go':[ +49, +52, +179, +182, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/token/bootstrap.go':[ +36, +41, +125, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/preflight/checks.go':[ +65, +268, +311, +319, +402, +437, +500, +548, +551, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/util/kubeconfig/kubeconfig.go':[ +51, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/util/version.go':[ +73, +76, +77, +78, +79, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/auth.go':[ +37, +38, +65, +84, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/options/container_runtime.go':[ +120, +122, +124, +125, +126, +128, +129, +130, +133, +135, +137, +138, +139, +142, +143, +144, +145, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/options/options.go':[ +121, +122, +125, +126, +127, +128, +132, +135, +136, +138, +140, +142, +144, +150, +152, +153, +154, +155, +157, +159, +161, +170, +180, +182, +186, +189, +193, +194, +195, +196, +197, +198, +199, +200, +201, +202, +204, +205, +206, +207, +208, +209, +210, +211, +214, +215, +216, +217, +218, +220, +221, +222, +225, +226, +227, +228, +229, +231, +232, +233, +234, +235, +238, +239, +241, +242, +243, +244, +245, +246, +247, +248, +249, +251, +253, +254, +255, +256, +257, +258, +259, +260, +263, +264, +265, +267, +271, +272, +273, +275, +276, +277, +278, +279, +280, +281, +282, +283, +284, +285, +286, +289, +290, +292, +295, +296, +297, +298, +299, +300, +302, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +160, +189, +213, +219, +220, +340, +347, +352, +439, +463, +681, +748, +878, +888, +935, +962, +969, +979, +1060, +], +'go-project:sources/go/kubernetes/cmd/kubemark/hollow-node.go':[ +70, +71, +72, +143, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/client_generator.go':[ +68, +69, +132, +163, +315, +331, +333, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/fake_client_generator.go':[ +32, +33, +94, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/generator_fake_for_clientset.go':[ +65, +66, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/generator_fake_for_group.go':[ +56, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/generator_fake_for_type.go':[ +119, +120, +121, +122, +123, +124, +128, +130, +132, +133, +134, +135, +136, +137, +138, +139, +140, +141, +142, +143, +144, +145, +146, +147, +148, +212, +216, +222, +223, +234, +235, +276, +288, +300, +312, +313, +332, +334, +335, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_clientset.go':[ +61, +77, +78, +79, +80, +81, +82, +83, +84, +85, +160, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_expansion.go':[ +45, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_group.go':[ +92, +93, +94, +96, +98, +99, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_type.go':[ +88, +89, +90, +94, +252, +293, +307, +353, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/scheme/generator_for_scheme.go':[ +70, +73, +91, +92, +93, +94, +95, +96, +97, +98, +99, +149, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/main.go':[ +52, +53, +54, +55, +57, +58, +59, +98, +155, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/test_apis/testgroup/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset/clientset.go':[ +24, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset/scheme/register.go':[ +43, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset/typed/testgroup/internalversion/fake/fake_testgroup_client.go':[ +22, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset/typed/testgroup/internalversion/fake/fake_testtype.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/testoutput/clientset_generated/test_internalclientset/typed/testgroup/internalversion/testtype.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/types/helpers.go':[ +118, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/types/types.go':[ +55, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +301, +432, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/main.go':[ +52, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/deepcopy-gen/main.go':[ +65, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/defaulter-gen/main.go':[ +59, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go':[ +58, +108, +109, +110, +112, +113, +116, +117, +118, +287, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +555, +595, +615, +672, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/namer.go':[ +105, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/package.go':[ +32, +162, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +81, +427, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/import-boss/main.go':[ +78, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/customargs.go':[ +28, +29, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/factory.go':[ +71, +72, +79, +80, +108, +154, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/generic.go':[ +109, +167, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/groupinterface.go':[ +82, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/informer.go':[ +67, +68, +83, +88, +126, +146, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/packages.go':[ +196, +198, +202, +203, +208, +209, +221, +253, +275, +301, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/types.go':[ +35, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/versioninterface.go':[ +62, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/lister-gen/generators/lister.go':[ +127, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/generators/openapi.go':[ +223, +471, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/main.go':[ +36, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/set-gen/main.go':[ +44, +], +'go-project:sources/go/kubernetes/cmd/linkcheck/links.go':[ +62, +162, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/mungedocs.go':[ +46, +47, +191, +202, +], +'go-project:sources/go/kubernetes/federation/apis/core/install/install.go':[ +33, +], +'go-project:sources/go/kubernetes/federation/apis/core/register.go':[ +35, +], +'go-project:sources/go/kubernetes/federation/apis/federation/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/federation/apis/federation/types.go':[ +25, +43, +44, +97, +], +'go-project:sources/go/kubernetes/federation/apis/federation/v1beta1/types.go':[ +24, +41, +44, +45, +66, +98, +], +'go-project:sources/go/kubernetes/federation/apis/federation/validation/validation.go':[ +35, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/clientset.go':[ +27, +28, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/autoscaling/v1/fake/fake_horizontalpodautoscaler.go':[ +35, +37, +39, +41, +49, +51, +59, +61, +76, +83, +93, +95, +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/autoscaling/v1/horizontalpodautoscaler.go':[ +44, +62, +63, +74, +75, +90, +115, +125, +126, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/batch/v1/fake/fake_job.go':[ +122, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/batch/v1/job.go':[ +62, +74, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/configmap.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/event.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/fake/fake_configmap.go':[ +112, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/fake/fake_event.go':[ +112, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/fake/fake_namespace.go':[ +114, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/fake/fake_secret.go':[ +112, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/fake/fake_service.go':[ +122, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/namespace.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/namespace_expansion.go':[ +26, +29, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/secret.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/core/v1/service.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/daemonset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/deployment.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/deployment_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/fake/fake_daemonset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/fake/fake_deployment.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/fake/fake_ingress.go':[ +122, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/fake/fake_replicaset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/ingress.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/extensions/v1beta1/replicaset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/federation/v1beta1/cluster.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/typed/federation/v1beta1/fake/fake_cluster.go':[ +114, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/clientset.go':[ +24, +25, +26, +27, +28, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/scheme/register.go':[ +47, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/autoscaling/internalversion/fake/fake_autoscaling_client.go':[ +22, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/autoscaling/internalversion/fake/fake_horizontalpodautoscaler.go':[ +35, +37, +39, +41, +49, +51, +59, +61, +83, +93, +95, +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/autoscaling/internalversion/horizontalpodautoscaler.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +139, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/batch/internalversion/fake/fake_batch_client.go':[ +22, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/batch/internalversion/fake/fake_job.go':[ +122, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/batch/internalversion/job.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/configmap.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/event.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/fake/fake_configmap.go':[ +112, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/fake/fake_core_client.go':[ +22, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/fake/fake_event.go':[ +112, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/fake/fake_namespace.go':[ +114, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/fake/fake_secret.go':[ +112, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/fake/fake_service.go':[ +122, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/namespace.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/namespace_expansion.go':[ +26, +29, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/secret.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/core/internalversion/service.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/daemonset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/deployment.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/fake/fake_daemonset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/fake/fake_deployment.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/fake/fake_extensions_client.go':[ +22, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/fake/fake_ingress.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/fake/fake_replicaset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/ingress.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/extensions/internalversion/replicaset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/federation/internalversion/cluster.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/federation/internalversion/fake/fake_cluster.go':[ +114, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_internalclientset/typed/federation/internalversion/fake/fake_federation_client.go':[ +22, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/autoscaling.go':[ +32, +43, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/batch.go':[ +32, +43, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/core.go':[ +25, +26, +45, +86, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/extensions.go':[ +35, +73, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/federation.go':[ +33, +45, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/install.go':[ +30, +33, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/options/options.go':[ +56, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/server.go':[ +99, +207, +382, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +139, +140, +141, +142, +151, +156, +158, +159, +165, +166, +170, +172, +178, +180, +187, +189, +198, +221, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/options/options.go':[ +111, +115, +116, +117, +118, +120, +121, +122, +123, +124, +125, +126, +127, +130, +], +'go-project:sources/go/kubernetes/federation/cmd/genfeddocs/gen_fed_docs.go':[ +29, +30, +], +'go-project:sources/go/kubernetes/federation/cmd/kubefed/app/kubefed.go':[ +41, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/dns.go':[ +110, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/rrchangeset.go':[ +103, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/rrsets.go':[ +39, +61, +84, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/stubs/route53api.go':[ +30, +32, +34, +55, +73, +101, +111, +124, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/zones.go':[ +37, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/rrchangeset.go':[ +86, +89, +111, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/rrsets.go':[ +48, +97, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/stubs/corednsapi.go':[ +45, +50, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/changes_service.go':[ +29, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/managed_zones_service.go':[ +32, +49, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/rrsets_list_call.go':[ +34, +39, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/rrsets_service.go':[ +32, +40, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs/changes_service.go':[ +28, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs/managed_zones_service.go':[ +28, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs/rrsets_list_call.go':[ +36, +40, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs/rrsets_service.go':[ +80, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/rrchangeset.go':[ +107, +111, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/rrset.go':[ +36, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/rrsets.go':[ +83, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/configmap.go':[ +37, +110, +115, +119, +123, +127, +132, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/crudtester/crudtester.go':[ +57, +178, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/daemonset.go':[ +40, +75, +113, +118, +122, +126, +130, +135, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/registry.go':[ +36, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/scheduling.go':[ +44, +45, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/secret.go':[ +37, +111, +116, +120, +124, +128, +133, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/cluster/cluster_client.go':[ +52, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/cluster/clustercontroller.go':[ +67, +150, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/deployment/deploymentcontroller.go':[ +112, +137, +139, +158, +190, +590, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +51, +53, +54, +55, +56, +57, +189, +206, +217, +234, +240, +242, +253, +261, +266, +342, +380, +382, +385, +452, +453, +479, +484, +486, +501, +538, +556, +559, +561, +606, +611, +621, +624, +628, +632, +639, +642, +646, +666, +667, +671, +699, +742, +771, +777, +791, +797, +799, +808, +811, +820, +824, +825, +835, +841, +842, +846, +856, +858, +891, +892, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/namespace/namespace_controller.go':[ +97, +160, +219, +454, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/replicaset/replicasetcontroller.go':[ +114, +139, +141, +160, +176, +190, +568, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/dns/dns.go':[ +157, +218, +219, +228, +229, +263, +282, +292, +325, +334, +335, +358, +361, +364, +371, +373, +390, +413, +417, +448, +454, +470, +471, +472, +473, +474, +482, +485, +510, +511, +512, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/ingress/ingress.go':[ +65, +76, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +144, +182, +412, +472, +528, +529, +542, +545, +574, +603, +626, +651, +687, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/sync/controller.go':[ +96, +109, +112, +359, +360, +364, +407, +427, +428, +432, +493, +494, +513, +514, +540, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/cluster_util.go':[ +82, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/clusterselector/clusterselector.go':[ +27, +53, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/deletionhelper/deletion_helper.go':[ +166, +177, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/eventsink/eventsink.go':[ +55, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_informer.go':[ +209, +299, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_updater.go':[ +72, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/handlers.go':[ +83, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/podanalyzer/pod_helper.go':[ +48, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/test/test_helper.go':[ +295, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +157, +158, +160, +163, +164, +166, +167, +168, +169, +170, +171, +223, +224, +229, +232, +288, +290, +299, +308, +314, +321, +327, +330, +348, +360, +367, +375, +383, +405, +434, +449, +521, +557, +601, +631, +644, +684, +794, +807, +822, +858, +1057, +1111, +1161, +1162, +1173, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/join.go':[ +85, +86, +87, +131, +159, +210, +233, +255, +268, +292, +321, +338, +344, +360, +391, +407, +492, +512, +537, +539, +547, +555, +566, +595, +646, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/kubefed.go':[ +34, +56, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/testing/testing.go':[ +43, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/unjoin.go':[ +65, +102, +115, +120, +208, +218, +306, +309, +312, +317, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/util/util.go':[ +147, +159, +241, +], +'go-project:sources/go/kubernetes/federation/plugin/pkg/admission/schedulingpolicy/query.go':[ +52, +], +'go-project:sources/go/kubernetes/federation/registry/cluster/etcd/etcd.go':[ +44, +], +'go-project:sources/go/kubernetes/federation/registry/cluster/registry.go':[ +50, +58, +62, +], +'go-project:sources/go/kubernetes/hack/cmd/teststale/teststale.go':[ +51, +], +'go-project:sources/go/kubernetes/pkg/api/annotation_key_constants.go':[ +89, +], +'go-project:sources/go/kubernetes/pkg/api/endpoints/util.go':[ +88, +90, +], +'go-project:sources/go/kubernetes/pkg/api/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/api/persistentvolume/util.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/api/register.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/api/resource/helpers.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/api/service/util.go':[ +45, +56, +67, +], +'go-project:sources/go/kubernetes/pkg/api/testapi/testapi.go':[ +150, +159, +175, +191, +200, +209, +218, +227, +236, +245, +254, +263, +272, +281, +290, +299, +308, +365, +525, +], +'go-project:sources/go/kubernetes/pkg/api/testing/conversion.go':[ +30, +46, +], +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +325, +418, +421, +515, +532, +564, +], +'go-project:sources/go/kubernetes/pkg/api/toleration.go':[ +22, +], +'go-project:sources/go/kubernetes/pkg/api/types.go':[ +197, +205, +284, +367, +463, +469, +481, +1009, +1120, +1561, +1563, +1922, +1995, +2189, +2198, +2759, +3100, +3228, +3732, +], +'go-project:sources/go/kubernetes/pkg/api/v1/annotation_key_constants.go':[ +89, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +261, +266, +272, +285, +294, +306, +312, +327, +336, +351, +368, +396, +433, +462, +676, +692, +], +'go-project:sources/go/kubernetes/pkg/api/v1/endpoints/util.go':[ +88, +90, +], +'go-project:sources/go/kubernetes/pkg/api/v1/helper/helpers.go':[ +254, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +292, +], +'go-project:sources/go/kubernetes/pkg/api/v1/resource/helpers.go':[ +30, +106, +120, +], +'go-project:sources/go/kubernetes/pkg/api/v1/service/util.go':[ +45, +56, +67, +], +'go-project:sources/go/kubernetes/pkg/api/v1/toleration.go':[ +19, +], +'go-project:sources/go/kubernetes/pkg/api/v1/types.go':[ +179, +202, +213, +233, +270, +275, +300, +317, +334, +342, +377, +382, +417, +430, +438, +499, +505, +517, +528, +534, +606, +627, +631, +634, +1175, +1185, +1478, +1665, +1667, +1702, +1708, +1712, +1732, +1742, +1761, +1786, +1811, +1820, +1827, +2142, +2158, +2167, +2178, +2192, +2201, +2212, +2215, +2257, +2265, +2276, +2344, +2397, +2413, +2420, +2426, +2435, +2440, +2444, +2464, +2491, +2498, +2503, +2522, +2602, +2628, +2742, +2777, +2800, +2816, +2949, +3003, +3018, +3033, +3142, +3149, +3151, +3154, +3349, +3353, +3364, +3371, +3537, +3679, +3687, +4085, +4088, +4089, +4091, +4287, +4428, +4464, +4472, +4526, +], +'go-project:sources/go/kubernetes/pkg/api/v1/validation/validation.go':[ +51, +53, +129, +138, +], +'go-project:sources/go/kubernetes/pkg/api/validation/events.go':[ +39, +44, +51, +54, +71, +], +'go-project:sources/go/kubernetes/pkg/api/validation/schema.go':[ +43, +256, +276, +364, +427, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +109, +118, +131, +137, +143, +344, +346, +347, +350, +360, +403, +407, +429, +432, +437, +440, +485, +488, +575, +578, +583, +695, +760, +785, +789, +818, +840, +842, +845, +856, +870, +1043, +1062, +1063, +1065, +1078, +1086, +1105, +1164, +1189, +1191, +1212, +1220, +1224, +1238, +1241, +1246, +1249, +1340, +1348, +1351, +1356, +1408, +1409, +1466, +1497, +1554, +1578, +1579, +1592, +1596, +1600, +1608, +1620, +1630, +1639, +1707, +1718, +1722, +1797, +1800, +1801, +1807, +1905, +1945, +1964, +1967, +1970, +2009, +2015, +2017, +2043, +2072, +2080, +2114, +2118, +2122, +2145, +2160, +2173, +2225, +2247, +2257, +2280, +2284, +2302, +2331, +2335, +2340, +2354, +2433, +2438, +2445, +2448, +2454, +2463, +2467, +2469, +2479, +2484, +2489, +2500, +2510, +2543, +2609, +2616, +2620, +2621, +2642, +2657, +2676, +2681, +2693, +2699, +2704, +2742, +2746, +2775, +2822, +2826, +2830, +2834, +2847, +2857, +2862, +2890, +2896, +2905, +2946, +2956, +2983, +2986, +2987, +3003, +3004, +3010, +3013, +3022, +3023, +3026, +3091, +3097, +3112, +3123, +3129, +3148, +3151, +3153, +3154, +3166, +3182, +3192, +3197, +3201, +3204, +3216, +3225, +3237, +3313, +3333, +3366, +3375, +3398, +3505, +3510, +3520, +3524, +3529, +3539, +3544, +3557, +3561, +3565, +3569, +3573, +3577, +3580, +3586, +3593, +3610, +3634, +3636, +3649, +3660, +3715, +3735, +3766, +3768, +3771, +3772, +3879, +3903, +3927, +3947, +3963, +3970, +3974, +4014, +4024, +4031, +4035, +4050, +4053, +4056, +4063, +4106, +4132, +4133, +4169, +4193, +4214, +4233, +4238, +], +'go-project:sources/go/kubernetes/pkg/apis/admission/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/install/install.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/v1alpha1/types.go':[ +42, +78, +137, +172, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/validation/validation.go':[ +31, +48, +57, +106, +109, +119, +122, +141, +152, +159, +174, +178, +179, +181, +186, +196, +204, +226, +232, +236, +244, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/conversion.go':[ +87, +114, +128, +162, +168, +179, +191, +210, +237, +245, +264, +270, +292, +296, +305, +309, +318, +328, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/types.go':[ +45, +69, +73, +121, +123, +182, +200, +316, +326, +428, +459, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/validation/validation.go':[ +43, +52, +61, +75, +112, +120, +124, +127, +135, +142, +154, +160, +161, +169, +185, +199, +], +'go-project:sources/go/kubernetes/pkg/apis/authentication/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/authorization/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/authorization/types.go':[ +78, +97, +], +'go-project:sources/go/kubernetes/pkg/apis/authorization/v1/types.go':[ +85, +111, +126, +129, +158, +161, +], +'go-project:sources/go/kubernetes/pkg/apis/authorization/v1beta1/types.go':[ +85, +111, +126, +129, +158, +161, +], +'go-project:sources/go/kubernetes/pkg/apis/authorization/validation/validation.go':[ +26, +29, +32, +35, +41, +44, +47, +75, +79, +82, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/types.go':[ +32, +36, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/conversion.go':[ +45, +52, +74, +116, +150, +173, +181, +191, +192, +200, +212, +213, +236, +244, +255, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/types.go':[ +49, +72, +84, +111, +115, +223, +228, +326, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v2alpha1/types.go':[ +140, +145, +270, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/validation/validation.go':[ +31, +34, +44, +47, +50, +53, +55, +64, +86, +91, +92, +97, +98, +100, +101, +118, +157, +208, +212, +216, +220, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/v1/types.go':[ +80, +113, +155, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/v2alpha1/types.go':[ +95, +100, +113, +118, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/validation/validation.go':[ +31, +62, +63, +70, +79, +98, +108, +111, +114, +129, +149, +150, +151, +163, +177, +184, +188, +202, +220, +230, +233, +], +'go-project:sources/go/kubernetes/pkg/apis/certificates/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/certificates/validation/validation.go':[ +50, +66, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/types.go':[ +433, +436, +439, +463, +495, +496, +498, +499, +503, +506, +592, +594, +838, +840, +842, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/defaults.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/types.go':[ +173, +175, +497, +499, +501, +558, +559, +561, +562, +566, +569, +570, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/types.go':[ +73, +77, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/conversion.go':[ +85, +104, +131, +137, +159, +167, +186, +190, +199, +203, +212, +228, +238, +248, +255, +266, +278, +283, +288, +294, +301, +307, +314, +317, +324, +332, +335, +342, +350, +370, +390, +401, +412, +423, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/types.go':[ +39, +63, +67, +216, +226, +328, +359, +493, +785, +812, +835, +853, +891, +895, +900, +922, +931, +1048, +1133, +1158, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/validation/validation.go':[ +44, +68, +77, +111, +117, +119, +128, +129, +130, +131, +132, +133, +134, +135, +137, +151, +164, +167, +172, +175, +178, +180, +181, +186, +191, +193, +203, +215, +273, +275, +279, +291, +296, +301, +324, +332, +336, +339, +345, +347, +356, +358, +359, +360, +361, +363, +375, +378, +488, +495, +496, +508, +535, +543, +556, +582, +585, +607, +615, +623, +624, +625, +626, +638, +648, +655, +663, +669, +678, +683, +687, +690, +704, +705, +715, +718, +719, +750, +754, +813, +903, +928, +930, +931, +945, +953, +972, +976, +999, +], +'go-project:sources/go/kubernetes/pkg/apis/imagepolicy/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/v1/conversion.go':[ +45, +50, +55, +61, +68, +74, +81, +99, +117, +137, +157, +168, +179, +190, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/v1/types.go':[ +86, +107, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/validation/validation.go':[ +39, +47, +66, +70, +93, +], +'go-project:sources/go/kubernetes/pkg/apis/policy/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/apis/policy/validation/validation.go':[ +42, +58, +59, +63, +64, +74, +75, +76, +77, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/helpers.go':[ +215, +271, +278, +285, +369, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/types.go':[ +45, +49, +53, +56, +57, +58, +59, +63, +75, +104, +105, +142, +155, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1alpha1/helpers.go':[ +75, +129, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1alpha1/types.go':[ +50, +53, +60, +64, +65, +66, +67, +72, +86, +117, +118, +159, +173, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1beta1/helpers.go':[ +75, +129, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1beta1/types.go':[ +50, +53, +60, +64, +65, +66, +71, +84, +115, +116, +157, +171, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/validation/validation.go':[ +35, +50, +57, +72, +85, +88, +94, +97, +104, +106, +109, +115, +137, +148, +150, +153, +159, +179, +181, +230, +], +'go-project:sources/go/kubernetes/pkg/apis/settings/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/apis/settings/validation/validation.go':[ +43, +50, +], +'go-project:sources/go/kubernetes/pkg/apis/storage/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/storage/validation/validation.go':[ +31, +40, +74, +], +'go-project:sources/go/kubernetes/pkg/auth/authorizer/abac/abac.go':[ +107, +202, +], +'go-project:sources/go/kubernetes/pkg/bootstrap/api/types.go':[ +70, +], +'go-project:sources/go/kubernetes/pkg/capabilities/capabilities.go':[ +32, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/clientset.go':[ +24, +125, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1/admissionregistration_client.go':[ +32, +37, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1/externaladmissionhookconfiguration.go':[ +43, +53, +59, +60, +70, +71, +93, +102, +103, +114, +115, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1/fake/fake_admissionregistration_client.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1/fake/fake_externaladmissionhookconfiguration.go':[ +34, +36, +38, +40, +47, +49, +58, +62, +69, +71, +78, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1/fake/fake_initializerconfiguration.go':[ +34, +36, +38, +40, +47, +49, +69, +78, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/admissionregistration/v1alpha1/initializerconfiguration.go':[ +43, +59, +60, +70, +71, +102, +103, +115, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/apps/v1beta1/controllerrevision.go':[ +43, +61, +62, +73, +74, +108, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/apps/v1beta1/deployment.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/apps/v1beta1/fake/fake_controllerrevision.go':[ +35, +39, +49, +73, +85, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/apps/v1beta1/fake/fake_deployment.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/apps/v1beta1/fake/fake_statefulset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/apps/v1beta1/statefulset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authentication/v1/fake/fake_tokenreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authentication/v1/tokenreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authentication/v1beta1/fake/fake_tokenreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authentication/v1beta1/tokenreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1/fake/fake_localsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1/fake/fake_selfsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1/fake/fake_subjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1/localsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1/selfsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1/subjectaccessreview_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/fake/fake_authorization_client.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/fake/fake_localsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/fake/fake_selfsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/fake/fake_subjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/localsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/selfsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/authorization/v1beta1/subjectaccessreview_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/autoscaling/v1/fake/fake_horizontalpodautoscaler.go':[ +35, +37, +39, +41, +49, +51, +59, +61, +76, +83, +93, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/autoscaling/v1/horizontalpodautoscaler.go':[ +44, +62, +63, +74, +75, +90, +115, +125, +126, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1/fake/fake_horizontalpodautoscaler.go':[ +35, +37, +39, +41, +49, +51, +59, +61, +83, +93, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/autoscaling/v2alpha1/horizontalpodautoscaler.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/batch/v1/fake/fake_job.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/batch/v1/job.go':[ +62, +74, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/batch/v2alpha1/cronjob.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/batch/v2alpha1/fake/fake_cronjob.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/certificates/v1beta1/certificatesigningrequest.go':[ +44, +60, +61, +71, +72, +86, +118, +119, +131, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/certificates/v1beta1/certificatesigningrequest_expansion.go':[ +24, +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/certificates/v1beta1/fake/fake_certificatesigningrequest.go':[ +34, +36, +38, +40, +47, +49, +56, +58, +78, +87, +89, +114, +116, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/certificates/v1beta1/fake/fake_certificatesigningrequest_expansion.go':[ +24, +26, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/componentstatus.go':[ +59, +70, +102, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/configmap.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/endpoints.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/event.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/event_expansion.go':[ +41, +126, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_componentstatus.go':[ +62, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_configmap.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_endpoints.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_event.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_event_expansion.go':[ +82, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_limitrange.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_namespace.go':[ +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_node.go':[ +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_persistentvolume.go':[ +58, +71, +114, +116, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_persistentvolumeclaim.go':[ +35, +39, +41, +49, +51, +59, +61, +76, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_pod.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_podtemplate.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_replicationcontroller.go':[ +35, +39, +41, +49, +51, +59, +61, +76, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_resourcequota.go':[ +61, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_secret.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_service.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/fake/fake_serviceaccount.go':[ +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/limitrange.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/namespace.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/namespace_expansion.go':[ +26, +29, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/node.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/persistentvolume.go':[ +60, +71, +86, +118, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/persistentvolumeclaim.go':[ +44, +62, +63, +74, +75, +90, +115, +125, +126, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/pod.go':[ +62, +74, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/pod_expansion.go':[ +35, +39, +44, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/podtemplate.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/replicationcontroller.go':[ +44, +62, +63, +74, +75, +90, +115, +125, +126, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/resourcequota.go':[ +62, +74, +125, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/secret.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/service.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/serviceaccount.go':[ +61, +73, +108, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/daemonset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/deployment.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/deployment_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/fake/fake_daemonset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/fake/fake_deployment.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/fake/fake_ingress.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/fake/fake_podsecuritypolicy.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/fake/fake_replicaset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/fake/fake_thirdpartyresource.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/ingress.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/podsecuritypolicy.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/replicaset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/thirdpartyresource.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/networking/v1/fake/fake_networkpolicy.go':[ +35, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/networking/v1/networkpolicy.go':[ +61, +73, +108, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/policy/v1beta1/fake/fake_poddisruptionbudget.go':[ +35, +39, +41, +49, +51, +59, +61, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/policy/v1beta1/poddisruptionbudget.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/clusterrole.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/clusterrolebinding.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/fake/fake_clusterrole.go':[ +34, +36, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/fake/fake_clusterrolebinding.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/fake/fake_role.go':[ +35, +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/fake/fake_rolebinding.go':[ +35, +37, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/role.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1alpha1/rolebinding.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/clusterrole.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/clusterrolebinding.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/fake/fake_clusterrole.go':[ +34, +36, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/fake/fake_clusterrolebinding.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/fake/fake_role.go':[ +35, +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/fake/fake_rolebinding.go':[ +35, +37, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/role.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/rbac/v1beta1/rolebinding.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/settings/v1alpha1/fake/fake_podpreset.go':[ +35, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/settings/v1alpha1/podpreset.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/storage/v1/fake/fake_storageclass.go':[ +34, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/storage/v1/storageclass.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/storage/v1beta1/fake/fake_storageclass.go':[ +34, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/storage/v1beta1/storageclass.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/clientset.go':[ +24, +26, +27, +28, +30, +32, +33, +36, +37, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/scheme/register.go':[ +56, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/scheme/register_custom.go':[ +26, +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/admissionregistration/internalversion/admissionregistration_client.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/admissionregistration/internalversion/externaladmissionhookconfiguration.go':[ +36, +37, +43, +59, +60, +70, +71, +93, +102, +103, +114, +115, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/admissionregistration/internalversion/fake/fake_admissionregistration_client.go':[ +22, +29, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/admissionregistration/internalversion/fake/fake_externaladmissionhookconfiguration.go':[ +34, +36, +38, +40, +47, +49, +58, +62, +69, +71, +78, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/admissionregistration/internalversion/fake/fake_initializerconfiguration.go':[ +34, +36, +38, +40, +47, +49, +58, +69, +71, +78, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/admissionregistration/internalversion/initializerconfiguration.go':[ +43, +59, +60, +70, +71, +102, +103, +115, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion/controllerrevision.go':[ +43, +61, +62, +73, +74, +108, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion/fake/fake_controllerrevision.go':[ +35, +39, +49, +85, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion/fake/fake_statefulset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/apps/internalversion/statefulset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion/fake/fake_authentication_client.go':[ +22, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion/fake/fake_tokenreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authentication/internalversion/tokenreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/fake/fake_authorization_client.go':[ +22, +29, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/fake/fake_localsubjectaccessreview_expansion.go':[ +25, +26, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/fake/fake_selfsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/fake/fake_subjectaccessreview_expansion.go':[ +25, +26, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/localsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/selfsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/authorization/internalversion/subjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion/fake/fake_horizontalpodautoscaler.go':[ +35, +37, +39, +41, +49, +51, +59, +61, +83, +93, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/autoscaling/internalversion/horizontalpodautoscaler.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +139, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion/cronjob.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion/fake/fake_cronjob.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion/fake/fake_job.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/batch/internalversion/job.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion/certificatesigningrequest.go':[ +44, +60, +61, +71, +72, +86, +118, +119, +131, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion/certificatesigningrequest_expansion.go':[ +22, +25, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion/fake/fake_certificatesigningrequest.go':[ +34, +36, +38, +40, +47, +49, +56, +58, +67, +78, +87, +89, +114, +116, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/certificates/internalversion/fake/fake_certificatesigningrequest_expansion.go':[ +24, +26, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/componentstatus.go':[ +59, +70, +102, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/configmap.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/endpoints.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/event.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/event_expansion.go':[ +42, +127, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_componentstatus.go':[ +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_configmap.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_endpoints.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_event.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_event_expansion.go':[ +82, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_limitrange.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_namespace.go':[ +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_node.go':[ +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_persistentvolume.go':[ +38, +47, +58, +89, +114, +116, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_persistentvolumeclaim.go':[ +35, +39, +41, +49, +51, +59, +61, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_pod.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_podtemplate.go':[ +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_replicationcontroller.go':[ +35, +39, +41, +49, +51, +59, +61, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_resourcequota.go':[ +61, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_secret.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_service.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/fake/fake_serviceaccount.go':[ +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/limitrange.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/namespace.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/namespace_expansion.go':[ +26, +29, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/node.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/persistentvolume.go':[ +60, +71, +86, +118, +151, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/persistentvolumeclaim.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/pod.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/pod_expansion.go':[ +32, +37, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/podtemplate.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/replicationcontroller.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/resourcequota.go':[ +62, +74, +125, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/secret.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/service.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/serviceaccount.go':[ +61, +73, +108, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/daemonset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/deployment.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/deployment_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake/fake_daemonset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake/fake_deployment.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake/fake_ingress.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake/fake_networkpolicy.go':[ +39, +49, +85, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake/fake_podsecuritypolicy.go':[ +34, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake/fake_replicaset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/fake/fake_thirdpartyresource.go':[ +34, +38, +40, +47, +49, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/ingress.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/networkpolicy.go':[ +43, +61, +62, +73, +74, +108, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/podsecuritypolicy.go':[ +43, +59, +60, +70, +71, +102, +103, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/replicaset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/thirdpartyresource.go':[ +43, +59, +60, +70, +71, +102, +103, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/networking/internalversion/fake/fake_networkpolicy.go':[ +35, +39, +49, +85, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/networking/internalversion/networkpolicy.go':[ +43, +61, +62, +73, +74, +108, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/policy/internalversion/fake/fake_poddisruptionbudget.go':[ +35, +39, +41, +49, +51, +59, +61, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/policy/internalversion/poddisruptionbudget.go':[ +44, +62, +63, +74, +75, +90, +125, +161, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/clusterrole.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/clusterrolebinding.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/fake/fake_clusterrole.go':[ +34, +105, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/fake/fake_clusterrolebinding.go':[ +34, +36, +38, +47, +80, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/fake/fake_role.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/fake/fake_rolebinding.go':[ +35, +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/role.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/rbac/internalversion/rolebinding.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/settings/internalversion/fake/fake_podpreset.go':[ +112, +114, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/settings/internalversion/podpreset.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/storage/internalversion/fake/fake_storageclass.go':[ +34, +105, +107, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/storage/internalversion/storageclass.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/admissionregistration/v1alpha1/externaladmissionhookconfiguration.go':[ +44, +63, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/admissionregistration/v1alpha1/initializerconfiguration.go':[ +44, +63, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/autoscaling/v1/horizontalpodautoscaler.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/autoscaling/v2alpha1/horizontalpodautoscaler.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/certificates/v1beta1/certificatesigningrequest.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/core/v1/persistentvolumeclaim.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/core/v1/replicationcontroller.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/factory.go':[ +26, +102, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/generic.go':[ +71, +73, +77, +79, +81, +85, +89, +101, +105, +119, +121, +127, +139, +141, +143, +145, +147, +149, +153, +157, +161, +163, +167, +171, +173, +177, +181, +185, +189, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/admissionregistration/interface.go':[ +22, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/admissionregistration/internalversion/externaladmissionhookconfiguration.go':[ +44, +63, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/admissionregistration/internalversion/initializerconfiguration.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/apps/internalversion/controllerrevision.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/autoscaling/interface.go':[ +22, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/autoscaling/internalversion/horizontalpodautoscaler.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/certificates/interface.go':[ +22, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/certificates/internalversion/certificatesigningrequest.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/core/internalversion/componentstatus.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/core/internalversion/persistentvolume.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/core/internalversion/persistentvolumeclaim.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/core/internalversion/replicationcontroller.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/core/internalversion/resourcequota.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/core/internalversion/serviceaccount.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/extensions/internalversion/networkpolicy.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/extensions/internalversion/podsecuritypolicy.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/extensions/internalversion/thirdpartyresource.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/factory.go':[ +26, +102, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/generic.go':[ +67, +69, +73, +75, +79, +83, +85, +89, +93, +95, +97, +99, +101, +103, +105, +107, +109, +111, +113, +115, +117, +119, +121, +123, +127, +129, +131, +133, +135, +137, +139, +143, +147, +151, +153, +155, +157, +161, +165, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/networking/internalversion/networkpolicy.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/policy/internalversion/poddisruptionbudget.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/rbac/internalversion/clusterrolebinding.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/client/leaderelection/resourcelock/configmaplock.go':[ +97, +], +'go-project:sources/go/kubernetes/pkg/client/leaderelection/resourcelock/endpointslock.go':[ +92, +], +'go-project:sources/go/kubernetes/pkg/client/listers/admissionregistration/internalversion/externaladmissionhookconfiguration.go':[ +48, +56, +], +'go-project:sources/go/kubernetes/pkg/client/listers/admissionregistration/internalversion/initializerconfiguration.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/client/listers/admissionregistration/v1alpha1/externaladmissionhookconfiguration.go':[ +48, +56, +], +'go-project:sources/go/kubernetes/pkg/client/listers/admissionregistration/v1alpha1/initializerconfiguration.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/client/listers/apps/internalversion/statefulset_expansion.go':[ +73, +], +'go-project:sources/go/kubernetes/pkg/client/listers/apps/v1beta1/controllerrevision.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/apps/v1beta1/statefulset_expansion.go':[ +73, +], +'go-project:sources/go/kubernetes/pkg/client/listers/authorization/internalversion/localsubjectaccessreview.go':[ +48, +56, +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/authorization/internalversion/selfsubjectaccessreview.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/client/listers/authorization/internalversion/subjectaccessreview.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/client/listers/authorization/v1/localsubjectaccessreview.go':[ +48, +56, +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/authorization/v1beta1/localsubjectaccessreview.go':[ +48, +56, +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/authorization/v1beta1/selfsubjectaccessreview.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/client/listers/autoscaling/internalversion/horizontalpodautoscaler.go':[ +48, +56, +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/autoscaling/v1/horizontalpodautoscaler.go':[ +56, +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/autoscaling/v2alpha1/horizontalpodautoscaler.go':[ +48, +56, +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/batch/internalversion/job_expansion.go':[ +61, +], +'go-project:sources/go/kubernetes/pkg/client/listers/batch/v1/job_expansion.go':[ +61, +], +'go-project:sources/go/kubernetes/pkg/client/listers/certificates/internalversion/certificatesigningrequest.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/client/listers/certificates/v1beta1/certificatesigningrequest.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/internalversion/persistentvolumeclaim.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/internalversion/replicationcontroller.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/internalversion/replicationcontroller_expansion.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/v1/persistentvolumeclaim.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/v1/replicationcontroller.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/v1/replicationcontroller_expansion.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/internalversion/daemonset_expansion.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/internalversion/deployment_expansion.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/internalversion/replicaset_expansion.go':[ +69, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/v1beta1/daemonset_expansion.go':[ +76, +110, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/v1beta1/deployment_expansion.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/v1beta1/replicaset_expansion.go':[ +69, +], +'go-project:sources/go/kubernetes/pkg/client/listers/policy/internalversion/poddisruptionbudget.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/policy/internalversion/poddisruptionbudget_expansion.go':[ +39, +70, +], +'go-project:sources/go/kubernetes/pkg/client/listers/policy/v1alpha1/poddisruptionbudget.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/policy/v1beta1/poddisruptionbudget.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/client/listers/policy/v1beta1/poddisruptionbudget_expansion.go':[ +39, +70, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/conditions.go':[ +40, +61, +98, +129, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/helper.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/cloud.go':[ +98, +133, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +83, +91, +95, +99, +103, +107, +111, +115, +120, +141, +144, +231, +250, +251, +253, +255, +261, +265, +273, +701, +705, +734, +869, +1012, +1022, +1064, +1239, +1334, +1462, +1515, +1517, +1587, +1606, +1609, +1854, +2210, +2382, +2457, +2545, +2594, +2610, +2613, +2668, +2785, +2790, +2847, +2850, +2922, +2945, +2953, +2989, +3083, +3085, +3187, +3208, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +65, +115, +116, +294, +378, +421, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go':[ +35, +181, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/tags.go':[ +94, +111, +155, +173, +191, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure.go':[ +59, +267, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_blobDiskController.go':[ +83, +84, +87, +103, +148, +174, +239, +240, +264, +300, +310, +389, +444, +449, +519, +533, +603, +619, +630, +657, +667, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_controllerCommon.go':[ +37, +40, +74, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_file.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_instances.go':[ +131, +143, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_loadbalancer.go':[ +39, +114, +115, +184, +404, +426, +501, +523, +747, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_managedDiskController.go':[ +39, +40, +91, +93, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_routes.go':[ +65, +69, +123, +127, +134, +141, +150, +154, +161, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_storage.go':[ +26, +41, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_util.go':[ +42, +43, +44, +45, +46, +132, +148, +276, +347, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_wrap.go':[ +136, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/cloudstack/cloudstack.go':[ +80, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/cloudstack/cloudstack_loadbalancer.go':[ +66, +67, +266, +413, +437, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/fake/fake.go':[ +48, +145, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce.go':[ +245, +458, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_annotations.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_backendservice.go':[ +87, +148, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_clusterid.go':[ +104, +106, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_forwardingrule.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_instancegroup.go':[ +73, +84, +104, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer.go':[ +59, +64, +113, +121, +133, +140, +154, +167, +175, +188, +196, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go':[ +45, +72, +113, +120, +137, +147, +153, +193, +204, +210, +237, +242, +244, +255, +260, +276, +281, +287, +300, +304, +309, +318, +474, +488, +553, +555, +561, +614, +629, +639, +644, +653, +740, +783, +846, +857, +869, +880, +931, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go':[ +39, +45, +59, +84, +86, +110, +118, +147, +150, +152, +160, +162, +166, +172, +186, +206, +207, +218, +270, +309, +328, +338, +365, +422, +442, +451, +452, +534, +627, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_naming.go':[ +36, +54, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_op.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_targetpool.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_targetproxy.go':[ +100, +129, +132, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go':[ +285, +567, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +271, +306, +308, +419, +495, +598, +599, +620, +746, +800, +896, +906, +909, +927, +1016, +1191, +1227, +1228, +1263, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_routes.go':[ +40, +97, +120, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_volumes.go':[ +219, +300, +324, +345, +398, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/photon/photon.go':[ +126, +156, +225, +384, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace.go':[ +490, +522, +538, +612, +654, +668, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +100, +172, +327, +343, +441, +513, +706, +733, +750, +751, +974, +1025, +1079, +1112, +1125, +1210, +1344, +1348, +1351, +1404, +1423, +1597, +1605, +1627, +1682, +1710, +1794, +1803, +1829, +1926, +1927, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere_metrics.go':[ +59, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere_util.go':[ +87, +115, +126, +153, +166, +255, +], +'go-project:sources/go/kubernetes/pkg/controller/bootstrap/bootstrapsigner.go':[ +126, +283, +], +'go-project:sources/go/kubernetes/pkg/controller/bootstrap/jws.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/controller/bootstrap/tokencleaner.go':[ +78, +], +'go-project:sources/go/kubernetes/pkg/controller/bootstrap/util.go':[ +55, +], +'go-project:sources/go/kubernetes/pkg/controller/certificates/approver/sarapprove.go':[ +30, +46, +62, +67, +74, +123, +], +'go-project:sources/go/kubernetes/pkg/controller/certificates/certificate_controller.go':[ +35, +61, +], +'go-project:sources/go/kubernetes/pkg/controller/certificates/signer/cfssl_signer.go':[ +30, +64, +], +'go-project:sources/go/kubernetes/pkg/controller/client_builder.go':[ +135, +139, +171, +172, +207, +215, +230, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +87, +251, +382, +429, +], +'go-project:sources/go/kubernetes/pkg/controller/controller_ref_manager.go':[ +80, +231, +242, +317, +353, +364, +452, +481, +488, +496, +499, +], +'go-project:sources/go/kubernetes/pkg/controller/controller_utils.go':[ +158, +196, +244, +431, +433, +519, +526, +538, +570, +642, +653, +838, +862, +875, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/cronjob_controller.go':[ +74, +204, +212, +218, +304, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/injection.go':[ +112, +168, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/utils.go':[ +83, +123, +179, +212, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +126, +130, +224, +562, +625, +665, +696, +750, +760, +791, +813, +820, +855, +911, +946, +976, +1037, +1039, +1117, +1124, +1168, +1175, +1182, +1199, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/update.go':[ +72, +85, +212, +235, +244, +284, +340, +351, +378, +384, +400, +436, +440, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/deployment_controller.go':[ +101, +105, +245, +439, +497, +516, +528, +589, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/progress.go':[ +37, +80, +92, +101, +111, +134, +163, +201, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/recreate.go':[ +28, +71, +92, +109, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/rollback.go':[ +31, +84, +88, +90, +92, +93, +97, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/rolling.go':[ +33, +64, +82, +90, +94, +96, +113, +115, +121, +150, +151, +154, +165, +171, +174, +188, +218, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/sync.go':[ +39, +51, +82, +86, +100, +106, +107, +108, +112, +137, +152, +153, +154, +156, +166, +172, +176, +182, +183, +200, +201, +206, +208, +212, +218, +225, +227, +228, +230, +268, +292, +294, +353, +363, +374, +380, +395, +493, +508, +519, +527, +536, +559, +563, +573, +587, +615, +618, +630, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/deployment_util.go':[ +110, +122, +134, +165, +210, +224, +250, +258, +259, +260, +280, +385, +416, +437, +444, +445, +488, +489, +491, +507, +508, +509, +517, +558, +583, +611, +639, +671, +691, +692, +713, +724, +736, +756, +891, +899, +903, +944, +946, +947, +958, +960, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/pod_util.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/replicaset_util.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/controller/disruption/disruption.go':[ +300, +306, +412, +417, +519, +537, +579, +630, +638, +652, +674, +702, +744, +], +'go-project:sources/go/kubernetes/pkg/controller/endpoint/endpoints_controller.go':[ +216, +225, +255, +318, +448, +479, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/errors.go':[ +23, +40, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/garbagecollector.go':[ +65, +84, +85, +99, +200, +225, +232, +289, +333, +339, +344, +356, +367, +405, +426, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/graph_builder.go':[ +125, +184, +319, +419, +471, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/metaonly/metaonly.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/operations.go':[ +75, +106, +132, +], +'go-project:sources/go/kubernetes/pkg/controller/history/controller_history.go':[ +147, +196, +220, +237, +268, +303, +311, +316, +320, +341, +374, +421, +431, +465, +], +'go-project:sources/go/kubernetes/pkg/controller/job/jobcontroller.go':[ +82, +86, +467, +493, +496, +508, +583, +628, +645, +], +'go-project:sources/go/kubernetes/pkg/controller/namespace/deletion/namespaced_resources_deleter.go':[ +113, +265, +338, +359, +367, +377, +382, +391, +400, +427, +440, +445, +448, +453, +473, +476, +482, +484, +487, +534, +536, +], +'go-project:sources/go/kubernetes/pkg/controller/namespace/namespace_controller.go':[ +75, +79, +], +'go-project:sources/go/kubernetes/pkg/controller/node/cidr_set.go':[ +143, +], +'go-project:sources/go/kubernetes/pkg/controller/node/controller_utils.go':[ +49, +61, +90, +247, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +225, +264, +334, +413, +442, +457, +484, +526, +554, +589, +683, +829, +859, +860, +861, +863, +865, +866, +869, +898, +903, +910, +], +'go-project:sources/go/kubernetes/pkg/controller/node/range_allocator.go':[ +65, +71, +212, +234, +], +'go-project:sources/go/kubernetes/pkg/controller/node/taint_controller.go':[ +253, +312, +318, +425, +437, +], +'go-project:sources/go/kubernetes/pkg/controller/node/testutil/test_utils.go':[ +269, +361, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/horizontal.go':[ +69, +213, +214, +224, +250, +253, +266, +269, +283, +286, +302, +310, +313, +329, +340, +387, +391, +409, +420, +430, +451, +458, +464, +468, +472, +476, +484, +491, +493, +500, +509, +510, +517, +518, +522, +530, +555, +561, +577, +607, +614, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/metrics/interfaces.go':[ +36, +44, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/metrics/legacy_metrics_client.go':[ +65, +113, +137, +155, +177, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/metrics/rest_metrics_client.go':[ +36, +59, +102, +103, +124, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/metrics/utilization.go':[ +26, +51, +57, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/replica_calculator.go':[ +39, +48, +75, +101, +144, +155, +157, +163, +170, +176, +181, +257, +270, +273, +], +'go-project:sources/go/kubernetes/pkg/controller/podgc/gc_controller.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/options/options.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set.go':[ +97, +99, +103, +179, +187, +223, +379, +511, +583, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set_utils.go':[ +36, +85, +116, +130, +141, +168, +], +'go-project:sources/go/kubernetes/pkg/controller/replication/replication_controller.go':[ +92, +94, +99, +174, +182, +218, +317, +374, +523, +597, +601, +624, +], +'go-project:sources/go/kubernetes/pkg/controller/replication/replication_controller_utils.go':[ +33, +34, +95, +126, +140, +151, +176, +177, +], +'go-project:sources/go/kubernetes/pkg/controller/resourcequota/replenishment_controller.go':[ +55, +72, +78, +107, +221, +224, +], +'go-project:sources/go/kubernetes/pkg/controller/resourcequota/resource_quota_controller.go':[ +92, +93, +99, +139, +361, +365, +], +'go-project:sources/go/kubernetes/pkg/controller/route/routecontroller.go':[ +63, +152, +158, +160, +183, +], +'go-project:sources/go/kubernetes/pkg/controller/service/servicecontroller.go':[ +109, +226, +314, +437, +655, +686, +691, +774, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/serviceaccounts_controller.go':[ +64, +127, +151, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/tokengetter.go':[ +60, +95, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/tokens_controller.go':[ +73, +84, +85, +250, +290, +307, +309, +315, +357, +372, +376, +377, +414, +431, +458, +465, +508, +510, +577, +616, +770, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_pod_control.go':[ +38, +166, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set.go':[ +89, +127, +150, +281, +302, +330, +359, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_control.go':[ +33, +177, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_status_updater.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_utils.go':[ +299, +302, +362, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/attach_detach_controller.go':[ +124, +245, +279, +518, +522, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/cache/actual_state_of_world.go':[ +59, +264, +437, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/cache/desired_state_of_world.go':[ +63, +267, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go':[ +112, +138, +188, +220, +233, +254, +262, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/statusupdater/node_status_updater.go':[ +102, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/testing/testvolumespec.go':[ +261, +376, +380, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/util/util.go':[ +34, +103, +127, +181, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/index.go':[ +38, +53, +74, +163, +205, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/options/options.go':[ +69, +71, +72, +78, +79, +86, +87, +88, +89, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller.go':[ +235, +257, +272, +280, +292, +301, +311, +315, +319, +334, +346, +355, +356, +372, +383, +393, +410, +420, +450, +458, +467, +475, +480, +495, +512, +515, +537, +565, +572, +591, +646, +651, +666, +688, +725, +748, +813, +856, +886, +892, +898, +904, +922, +989, +1033, +1049, +1068, +1114, +1276, +1323, +1331, +1376, +1404, +1455, +1478, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller_base.go':[ +74, +127, +274, +403, +437, +444, +500, +504, +514, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/volume_host.go':[ +52, +56, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/aws/aws_credentials.go':[ +59, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/gcp/metadata.go':[ +201, +202, +203, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/keyring.go':[ +312, +319, +], +'go-project:sources/go/kubernetes/pkg/features/kube_features.go':[ +65, +], +'go-project:sources/go/kubernetes/pkg/generated/bindata.go':[ +209, +221, +3681, +3686, +3698, +7157, +7162, +7174, +7287, +7292, +7304, +7413, +9548, +9553, +9565, +9671, +9676, +9688, +9794, +9799, +9811, +9856, +9861, +9873, +9918, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/admission/configuration/external_admission_hook_manager.go':[ +39, +58, +65, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/admission/configuration/initializer_manager.go':[ +65, +74, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/authenticator/config.go':[ +133, +154, +162, +247, +263, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/default_storage_factory_builder.go':[ +35, +36, +37, +45, +49, +59, +62, +68, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/options/authentication.go':[ +368, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/options/authorization.go':[ +74, +86, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/options/serving.go':[ +34, +88, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/server/insecure_handler.go':[ +42, +49, +50, +87, +112, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/annotate.go':[ +72, +73, +85, +131, +135, +300, +312, +318, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply.go':[ +69, +80, +90, +115, +116, +117, +118, +119, +120, +122, +124, +148, +263, +268, +403, +428, +507, +556, +560, +566, +589, +603, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_edit_last_applied.go':[ +99, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go':[ +79, +101, +160, +165, +168, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_view_last_applied.go':[ +76, +77, +119, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/attach.go':[ +84, +92, +98, +133, +292, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/auth/cani.go':[ +32, +37, +110, +233, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/autoscale.go':[ +37, +41, +67, +68, +71, +72, +82, +168, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/certificates.go':[ +98, +143, +163, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo.go':[ +97, +99, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go':[ +46, +150, +196, +197, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/cmd.go':[ +99, +109, +150, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/config.go':[ +46, +47, +53, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go':[ +73, +90, +103, +118, +120, +122, +127, +277, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_cluster.go':[ +66, +79, +80, +82, +84, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_context.go':[ +57, +73, +74, +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/delete_context.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/get_contexts.go':[ +68, +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go':[ +47, +48, +49, +50, +51, +52, +104, +105, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/rename_context.go':[ +120, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/set.go':[ +51, +53, +69, +130, +230, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/unset.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/view.go':[ +74, +84, +99, +100, +160, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/convert.go':[ +83, +84, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create.go':[ +54, +83, +88, +158, +196, +281, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_clusterrole.go':[ +40, +83, +85, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_clusterrolebinding.go':[ +37, +43, +59, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_configmap.go':[ +78, +79, +80, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_deployment.go':[ +83, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_pdb.go':[ +64, +65, +66, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_quota.go':[ +37, +62, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_role.go':[ +46, +55, +135, +307, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_rolebinding.go':[ +43, +60, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_secret.go':[ +64, +89, +90, +91, +142, +148, +208, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_service.go':[ +82, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/delete.go':[ +153, +154, +155, +156, +157, +158, +159, +226, +242, +247, +292, +322, +323, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/describe.go':[ +97, +98, +103, +178, +183, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/drain.go':[ +84, +166, +169, +186, +188, +189, +190, +347, +486, +558, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/edit.go':[ +111, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/exec.go':[ +62, +89, +97, +103, +176, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/expose.go':[ +57, +63, +69, +87, +88, +99, +101, +102, +103, +104, +107, +108, +109, +110, +112, +113, +123, +173, +263, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/get.go':[ +44, +116, +133, +134, +135, +136, +142, +191, +228, +545, +560, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/label.go':[ +72, +73, +74, +129, +133, +334, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/logs.go':[ +69, +94, +111, +112, +113, +114, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/patch.go':[ +46, +48, +64, +77, +109, +117, +139, +230, +241, +269, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/plugin.go':[ +72, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/proxy.go':[ +84, +86, +87, +88, +89, +92, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/replace.go':[ +48, +82, +83, +84, +94, +150, +173, +204, +228, +229, +276, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollingupdate.go':[ +47, +81, +90, +91, +92, +96, +98, +99, +100, +101, +142, +250, +264, +281, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollout/rollout_history.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollout/rollout_pause.go':[ +37, +148, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollout/rollout_resume.go':[ +37, +153, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollout/rollout_status.go':[ +75, +102, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollout/rollout_undo.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/run.go':[ +62, +71, +87, +92, +113, +116, +119, +121, +122, +126, +127, +128, +129, +130, +131, +133, +134, +139, +176, +192, +238, +262, +517, +552, +572, +629, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/scale.go':[ +80, +81, +84, +95, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/helper.go':[ +139, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_image.go':[ +35, +57, +108, +109, +162, +167, +190, +234, +244, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_resources.go':[ +40, +60, +84, +117, +118, +119, +120, +123, +124, +171, +230, +240, +241, +246, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_selector.go':[ +37, +62, +65, +66, +70, +71, +94, +183, +190, +223, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_subject.go':[ +50, +55, +87, +101, +102, +103, +107, +231, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/stop.go':[ +73, +74, +80, +108, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/taint.go':[ +69, +70, +118, +119, +126, +315, +383, +390, +398, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/templates/templates.go':[ +52, +63, +67, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/testing/fake.go':[ +175, +180, +185, +341, +345, +385, +461, +465, +483, +613, +635, +643, +647, +698, +716, +723, +736, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/top_node.go':[ +53, +55, +94, +112, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/top_pod.go':[ +96, +97, +98, +120, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/cached_discovery.go':[ +45, +68, +115, +260, +261, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +129, +155, +212, +237, +267, +357, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory.go':[ +68, +187, +229, +236, +241, +287, +444, +489, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_builder.go':[ +43, +52, +72, +104, +121, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_client_access.go':[ +75, +110, +137, +263, +270, +373, +417, +604, +614, +621, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_object_mapping.go':[ +235, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +158, +160, +162, +164, +232, +401, +406, +407, +426, +435, +439, +443, +517, +538, +542, +614, +619, +645, +684, +721, +726, +773, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/openapi/openapi.go':[ +358, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/printing.go':[ +37, +38, +42, +47, +48, +50, +66, +67, +72, +76, +112, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/sanity/cmd_sanity.go':[ +88, +102, +135, +146, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper.go':[ +31, +64, +], +'go-project:sources/go/kubernetes/pkg/kubectl/explain.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/kubectl/history.go':[ +258, +268, +284, +], +'go-project:sources/go/kubernetes/pkg/kubectl/metricsutil/metrics_client.go':[ +56, +67, +100, +129, +], +'go-project:sources/go/kubernetes/pkg/kubectl/metricsutil/metrics_printer.go':[ +54, +77, +107, +], +'go-project:sources/go/kubernetes/pkg/kubectl/pdb.go':[ +155, +], +'go-project:sources/go/kubernetes/pkg/kubectl/proxy_server.go':[ +147, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/builder.go':[ +109, +408, +705, +757, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/categories.go':[ +47, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/helper.go':[ +78, +171, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/mapper.go':[ +124, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/result.go':[ +219, +230, +291, +302, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/selector.go':[ +38, +50, +58, +84, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/visitor.go':[ +119, +398, +444, +516, +616, +629, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rollback.go':[ +69, +78, +99, +136, +214, +260, +277, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +121, +124, +134, +233, +313, +346, +397, +399, +408, +455, +464, +481, +499, +514, +573, +593, +682, +685, +696, +773, +808, +833, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rollout_status.go':[ +73, +82, +85, +88, +108, +111, +133, +145, +], +'go-project:sources/go/kubernetes/pkg/kubectl/run.go':[ +685, +], +'go-project:sources/go/kubernetes/pkg/kubectl/scale.go':[ +50, +122, +152, +169, +193, +267, +291, +338, +359, +376, +391, +416, +460, +486, +503, +], +'go-project:sources/go/kubernetes/pkg/kubectl/service.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/kubectl/stop.go':[ +100, +144, +160, +222, +223, +224, +229, +293, +330, +399, +468, +497, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/cri/services.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/cri/testing/fake_image_service.go':[ +138, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/cri/testing/fake_runtime_service.go':[ +232, +365, +393, +397, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/stats/v1alpha1/types.go':[ +73, +120, +223, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/well_known_labels.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/cadvisor_linux.go':[ +109, +167, +171, +179, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/cadvisor_unsupported.go':[ +44, +48, +52, +56, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/cadvisor_windows.go':[ +41, +45, +49, +53, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/testing/cadvisor_fake.go':[ +40, +44, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/testing/cadvisor_mock.go':[ +45, +50, +], +'go-project:sources/go/kubernetes/pkg/kubelet/certificate/certificate_manager.go':[ +173, +354, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_linux.go':[ +183, +223, +236, +336, +738, +740, +741, +742, +744, +799, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_unsupported.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_windows.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/node_container_manager.go':[ +83, +93, +104, +110, +119, +130, +183, +205, +229, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/pod_container_manager_linux.go':[ +183, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/qos_container_manager_linux.go':[ +59, +193, +205, +210, +228, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/apiserver.go':[ +33, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/common.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/config.go':[ +141, +213, +342, +362, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/file_linux.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/http.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/kubelet/configmap/configmap_manager.go':[ +109, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/container_gc.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/helpers.go':[ +51, +203, +210, +286, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/runtime.go':[ +90, +116, +133, +165, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/fake_runtime.go':[ +243, +271, +314, +329, +343, +458, +474, +494, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/fake_runtime_helper.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/runtime_mock.go':[ +68, +93, +98, +103, +], +'go-project:sources/go/kubernetes/pkg/kubelet/disk_manager.go':[ +94, +107, +115, +118, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_checkpoint.go':[ +95, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_container.go':[ +94, +306, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_image.go':[ +99, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go':[ +84, +139, +145, +272, +369, +517, +518, +537, +584, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_service.go':[ +148, +149, +150, +203, +224, +494, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_stats.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_streaming.go':[ +48, +53, +61, +79, +139, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/exec.go':[ +39, +66, +137, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/helpers.go':[ +160, +250, +341, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/helpers_linux.go':[ +34, +67, +78, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/helpers_unsupported.go':[ +32, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/helpers_windows.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/fake_client.go':[ +49, +90, +517, +532, +647, +678, +693, +712, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/instrumented_client.go':[ +56, +74, +137, +145, +181, +190, +208, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/kube_docker_client.go':[ +67, +86, +134, +302, +324, +343, +378, +432, +475, +491, +525, +541, +543, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/legacy.go':[ +67, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/remote/docker_service.go':[ +50, +62, +70, +78, +86, +94, +102, +110, +118, +126, +134, +142, +150, +175, +179, +187, +195, +203, +211, +220, +224, +228, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/security_context.go':[ +34, +59, +86, +127, +137, +160, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/securitycontext/fake.go':[ +32, +34, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/securitycontext/provider.go':[ +44, +57, +98, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/securitycontext/types.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/api/types.go':[ +35, +37, +39, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/eviction_manager.go':[ +86, +130, +151, +179, +202, +214, +236, +338, +428, +429, +455, +456, +497, +517, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/helpers.go':[ +57, +107, +190, +204, +454, +670, +725, +740, +753, +774, +787, +821, +828, +837, +844, +861, +862, +880, +901, +902, +919, +964, +1022, +1023, +1030, +1031, +1032, +1033, +1044, +1052, +1053, +1057, +1058, +1059, +1060, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/threshold_notifier_linux.go':[ +45, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/threshold_notifier_unsupported.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/types.go':[ +45, +49, +56, +88, +], +'go-project:sources/go/kubernetes/pkg/kubelet/gpu/nvidia/nvidia_gpu_manager.go':[ +106, +126, +127, +159, +237, +242, +253, +], +'go-project:sources/go/kubernetes/pkg/kubelet/images/image_gc_manager.go':[ +132, +141, +203, +270, +277, +322, +330, +350, +], +'go-project:sources/go/kubernetes/pkg/kubelet/images/image_manager.go':[ +43, +76, +86, +128, +133, +142, +], +'go-project:sources/go/kubernetes/pkg/kubelet/images/puller.go':[ +46, +76, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +192, +199, +250, +255, +266, +272, +281, +295, +388, +399, +401, +409, +490, +505, +523, +540, +682, +693, +696, +718, +771, +778, +811, +812, +1124, +1309, +1383, +1387, +1388, +1624, +1651, +1657, +2196, +2197, +2205, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_cadvisor.go':[ +27, +49, +65, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_network.go':[ +52, +70, +101, +120, +137, +191, +198, +367, +377, +389, +392, +393, +396, +397, +411, +417, +423, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_node_status.go':[ +213, +436, +445, +449, +709, +904, +911, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +111, +119, +125, +185, +211, +296, +313, +424, +483, +518, +679, +722, +743, +964, +968, +976, +977, +978, +980, +997, +1014, +1016, +1018, +1022, +1031, +1221, +1274, +1418, +1429, +1443, +1461, +1498, +1511, +1518, +1552, +1665, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_resources.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_volumes.go':[ +67, +74, +112, +116, +128, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/fake_kuberuntime_manager.go':[ +74, +94, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/instrumented_services.go':[ +80, +116, +134, +197, +206, +215, +224, +260, +278, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_container.go':[ +56, +81, +104, +109, +124, +160, +214, +273, +346, +362, +393, +416, +466, +498, +537, +591, +620, +630, +646, +664, +706, +718, +734, +748, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc.go':[ +42, +214, +372, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_logs.go':[ +121, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go':[ +372, +397, +408, +436, +508, +554, +564, +571, +664, +667, +670, +691, +725, +737, +759, +772, +779, +819, +889, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_sandbox.go':[ +62, +133, +144, +205, +222, +252, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/labels.go':[ +126, +135, +185, +194, +197, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/security_context.go':[ +29, +91, +], +'go-project:sources/go/kubernetes/pkg/kubelet/lifecycle/admission_failure_handler_stub.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/kubelet/lifecycle/fake_handler_runner.go':[ +38, +48, +50, +52, +], +'go-project:sources/go/kubernetes/pkg/kubelet/lifecycle/handlers.go':[ +46, +54, +61, +68, +], +'go-project:sources/go/kubernetes/pkg/kubelet/lifecycle/predicate.go':[ +35, +45, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/cni/cni.go':[ +174, +226, +265, +266, +283, +301, +318, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hairpin/hairpin.go':[ +66, +79, +84, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/fake_iptables.go':[ +60, +128, +195, +319, +323, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport.go':[ +195, +196, +197, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport_manager.go':[ +277, +289, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport_syncer.go':[ +43, +156, +177, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/testing/fake.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_linux.go':[ +63, +64, +127, +188, +264, +313, +337, +393, +417, +471, +485, +486, +550, +551, +582, +587, +744, +757, +763, +771, +777, +810, +817, +819, +824, +830, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_unsupported.go':[ +37, +45, +53, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/plugins.go':[ +73, +81, +161, +212, +240, +248, +381, +388, +394, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/testing/mock_network_plugin.go':[ +17, +74, +85, +105, +], +'go-project:sources/go/kubernetes/pkg/kubelet/oom_watcher.go':[ +67, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pleg/generic.go':[ +160, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod/mirror_client.go':[ +87, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod_container_deletor.go':[ +61, +63, +100, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod_workers.go':[ +49, +300, +332, +], +'go-project:sources/go/kubernetes/pkg/kubelet/preemption/preemption.go':[ +44, +56, +66, +67, +101, +125, +130, +134, +135, +140, +], +'go-project:sources/go/kubernetes/pkg/kubelet/prober/prober.go':[ +76, +103, +108, +119, +142, +234, +], +'go-project:sources/go/kubernetes/pkg/kubelet/remote/remote_runtime.go':[ +72, +176, +293, +364, +424, +428, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/image.go':[ +244, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/log.go':[ +78, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +111, +115, +305, +766, +801, +867, +1158, +1274, +1276, +1278, +1280, +1334, +1391, +1544, +1775, +1811, +1916, +2092, +2100, +2149, +2156, +2336, +2391, +2408, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/version.go':[ +90, +99, +108, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/app-interface.go':[ +70, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/fake-app-interface.go':[ +116, +208, +], +'go-project:sources/go/kubernetes/pkg/kubelet/runonce.go':[ +137, +], +'go-project:sources/go/kubernetes/pkg/kubelet/secret/secret_manager.go':[ +109, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/auth.go':[ +41, +59, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/httpstream.go':[ +36, +176, +200, +247, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/portforward.go':[ +41, +44, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/websocket.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/attach.go':[ +36, +41, +49, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/exec.go':[ +38, +44, +52, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/httpstream.go':[ +105, +125, +159, +208, +259, +310, +358, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/websocket.go':[ +73, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/server.go':[ +133, +151, +173, +175, +182, +183, +184, +196, +197, +251, +257, +520, +531, +802, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/stats/handler.go':[ +42, +44, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/stats/resource_analyzer.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/stats/summary.go':[ +54, +233, +370, +393, +415, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/stats/volume_stat_calculator.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/streaming/server.go':[ +63, +64, +334, +338, +], +'go-project:sources/go/kubernetes/pkg/kubelet/status/generate.go':[ +36, +91, +], +'go-project:sources/go/kubernetes/pkg/kubelet/status/status_manager.go':[ +113, +234, +391, +437, +459, +529, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util/csr/csr.go':[ +49, +79, +135, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util/util_linux.go':[ +70, +75, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volume_host.go':[ +106, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/cache/actual_state_of_world.go':[ +61, +305, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/cache/desired_state_of_world.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go':[ +238, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/reconciler/reconciler.go':[ +181, +183, +193, +207, +209, +227, +229, +251, +253, +278, +280, +290, +299, +301, +387, +391, +406, +482, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/volume_manager.go':[ +375, +], +'go-project:sources/go/kubernetes/pkg/master/client_ca_hook.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/master/controller.go':[ +80, +122, +123, +139, +177, +178, +182, +209, +228, +241, +302, +321, +343, +399, +438, +], +'go-project:sources/go/kubernetes/pkg/master/master.go':[ +173, +213, +218, +249, +251, +253, +262, +279, +286, +300, +307, +317, +321, +], +'go-project:sources/go/kubernetes/pkg/master/thirdparty/thirdparty.go':[ +28, +88, +182, +217, +224, +247, +349, +403, +410, +412, +417, +448, +], +'go-project:sources/go/kubernetes/pkg/master/thirdparty/tprregistration_controller.go':[ +69, +71, +73, +81, +], +'go-project:sources/go/kubernetes/pkg/master/tunneler/ssh.go':[ +70, +], +'go-project:sources/go/kubernetes/pkg/metrics/metrics_grabber.go':[ +54, +89, +115, +124, +126, +], +'go-project:sources/go/kubernetes/pkg/printers/customcolumn.go':[ +57, +71, +76, +106, +107, +111, +114, +119, +], +'go-project:sources/go/kubernetes/pkg/printers/humanreadable.go':[ +75, +169, +636, +689, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +179, +189, +211, +219, +306, +334, +424, +479, +506, +520, +522, +524, +526, +645, +827, +838, +925, +967, +1052, +1127, +1254, +1267, +1270, +1305, +1330, +1415, +1424, +1437, +1446, +1487, +1511, +1587, +1601, +1688, +1714, +1732, +1746, +1786, +1815, +1850, +1899, +1980, +2055, +2103, +2191, +2203, +2230, +2259, +2291, +2330, +2337, +2359, +2444, +2469, +2479, +2494, +2517, +2571, +2585, +2610, +2621, +2622, +2645, +2683, +2693, +2699, +2710, +2764, +2786, +2795, +2800, +2821, +2835, +2845, +2871, +2908, +2950, +2978, +3005, +3039, +3229, +3302, +3421, +3432, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +77, +79, +85, +86, +104, +119, +251, +252, +307, +344, +439, +449, +492, +982, +1008, +1037, +1067, +1120, +1245, +1255, +1448, +1494, +1546, +1612, +1629, +1643, +1674, +1762, +1801, +1820, +1844, +1848, +1858, +1868, +1878, +1897, +], +'go-project:sources/go/kubernetes/pkg/printers/name.go':[ +67, +], +'go-project:sources/go/kubernetes/pkg/printers/printers.go':[ +32, +], +'go-project:sources/go/kubernetes/pkg/printers/storage/storage.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/printers/tabwriter.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/printers/versioned.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/proxy/config/config.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/proxy/healthcheck/healthcheck.go':[ +79, +271, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +322, +519, +572, +582, +603, +638, +800, +818, +971, +972, +1058, +1059, +1578, +1611, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/port_allocator.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +156, +157, +164, +182, +185, +224, +230, +237, +238, +244, +251, +260, +293, +359, +553, +560, +575, +586, +593, +599, +603, +613, +685, +696, +717, +733, +737, +751, +763, +794, +868, +880, +886, +892, +896, +902, +913, +953, +986, +1033, +1068, +1085, +1097, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxysocket.go':[ +76, +91, +246, +275, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/roundrobin.go':[ +84, +93, +135, +166, +271, +300, +309, +327, +346, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxier.go':[ +141, +155, +172, +234, +245, +354, +452, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +109, +122, +493, +543, +565, +584, +594, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/roundrobin.go':[ +84, +93, +125, +156, +261, +290, +299, +316, +335, +], +'go-project:sources/go/kubernetes/pkg/quota/evaluator/core/persistent_volume_claims.go':[ +83, +86, +114, +184, +], +'go-project:sources/go/kubernetes/pkg/quota/evaluator/core/pods.go':[ +74, +107, +111, +], +'go-project:sources/go/kubernetes/pkg/quota/generic/evaluator.go':[ +33, +34, +66, +], +'go-project:sources/go/kubernetes/pkg/quota/resources.go':[ +218, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/externaladmissionhookconfiguration/doc.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/externaladmissionhookconfiguration/strategy.go':[ +57, +70, +85, +86, +87, +91, +100, +114, +118, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/doc.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/strategy.go':[ +85, +86, +87, +114, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/rest/storage_apiserver.go':[ +27, +33, +34, +35, +39, +45, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/controllerrevision/strategy.go':[ +98, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/rest/storage_apps.go':[ +34, +35, +36, +40, +47, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/statefulset/storage/storage.go':[ +79, +84, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/statefulset/strategy.go':[ +120, +148, +], +'go-project:sources/go/kubernetes/pkg/registry/authentication/rest/storage_authentication.go':[ +36, +42, +43, +47, +51, +58, +72, +], +'go-project:sources/go/kubernetes/pkg/registry/authentication/tokenreview/storage.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/registry/authorization/localsubjectaccessreview/rest.go':[ +43, +56, +], +'go-project:sources/go/kubernetes/pkg/registry/authorization/rest/storage_authorization.go':[ +38, +43, +44, +48, +53, +60, +77, +], +'go-project:sources/go/kubernetes/pkg/registry/authorization/selfsubjectaccessreview/rest.go':[ +43, +60, +], +'go-project:sources/go/kubernetes/pkg/registry/authorization/subjectaccessreview/rest.go':[ +43, +], +'go-project:sources/go/kubernetes/pkg/registry/authorization/util/helpers.go':[ +25, +40, +], +'go-project:sources/go/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go':[ +86, +91, +], +'go-project:sources/go/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go':[ +82, +124, +], +'go-project:sources/go/kubernetes/pkg/registry/autoscaling/rest/storage_autoscaling.go':[ +33, +34, +35, +39, +43, +50, +62, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/cronjob/storage/storage.go':[ +72, +77, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/job/storage/storage.go':[ +95, +100, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/rest/storage_batch.go':[ +35, +36, +37, +41, +49, +56, +68, +], +'go-project:sources/go/kubernetes/pkg/registry/certificates/certificates/registry.go':[ +31, +34, +50, +69, +73, +], +'go-project:sources/go/kubernetes/pkg/registry/certificates/certificates/storage/storage.go':[ +85, +99, +], +'go-project:sources/go/kubernetes/pkg/registry/certificates/certificates/strategy.go':[ +152, +177, +], +'go-project:sources/go/kubernetes/pkg/registry/certificates/rest/storage_certificates.go':[ +32, +33, +34, +38, +45, +], +'go-project:sources/go/kubernetes/pkg/registry/core/configmap/registry.go':[ +49, +58, +62, +], +'go-project:sources/go/kubernetes/pkg/registry/core/endpoint/registry.go':[ +48, +56, +60, +], +'go-project:sources/go/kubernetes/pkg/registry/core/namespace/registry.go':[ +49, +57, +61, +], +'go-project:sources/go/kubernetes/pkg/registry/core/namespace/storage/storage.go':[ +80, +95, +99, +116, +137, +201, +220, +225, +234, +], +'go-project:sources/go/kubernetes/pkg/registry/core/namespace/strategy.go':[ +129, +146, +], +'go-project:sources/go/kubernetes/pkg/registry/core/node/registry.go':[ +49, +68, +], +'go-project:sources/go/kubernetes/pkg/registry/core/node/rest/proxy.go':[ +64, +74, +78, +], +'go-project:sources/go/kubernetes/pkg/registry/core/node/storage/storage.go':[ +63, +68, +73, +87, +], +'go-project:sources/go/kubernetes/pkg/registry/core/node/strategy.go':[ +176, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolume/storage/storage.go':[ +79, +84, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolume/strategy.go':[ +100, +110, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolumeclaim/storage/storage.go':[ +79, +84, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolumeclaim/strategy.go':[ +74, +76, +96, +97, +106, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/rest/subresources.go':[ +63, +73, +95, +132, +180, +192, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/storage/eviction.go':[ +56, +74, +149, +154, +157, +179, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/storage/storage.go':[ +66, +81, +141, +157, +162, +191, +213, +218, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/strategy.go':[ +109, +142, +228, +], +'go-project:sources/go/kubernetes/pkg/registry/core/replicationcontroller/registry.go':[ +34, +36, +37, +38, +53, +64, +68, +76, +84, +], +'go-project:sources/go/kubernetes/pkg/registry/core/replicationcontroller/storage/storage.go':[ +112, +117, +133, +141, +], +'go-project:sources/go/kubernetes/pkg/registry/core/replicationcontroller/strategy.go':[ +124, +127, +181, +], +'go-project:sources/go/kubernetes/pkg/registry/core/resourcequota/storage/storage.go':[ +79, +84, +], +'go-project:sources/go/kubernetes/pkg/registry/core/resourcequota/strategy.go':[ +99, +109, +], +'go-project:sources/go/kubernetes/pkg/registry/core/rest/storage_core.go':[ +91, +100, +105, +159, +162, +169, +172, +180, +], +'go-project:sources/go/kubernetes/pkg/registry/core/secret/registry.go':[ +49, +57, +61, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/allocator/storage/storage.go':[ +63, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/ipallocator/controller/repair.go':[ +63, +65, +139, +150, +156, +160, +166, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/portallocator/allocator.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/portallocator/controller/repair.go':[ +51, +133, +139, +143, +147, +149, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/proxy.go':[ +60, +70, +74, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/registry.go':[ +52, +68, +89, +95, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +84, +162, +236, +253, +312, +341, +354, +379, +470, +537, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/storage/storage.go':[ +86, +91, +], +'go-project:sources/go/kubernetes/pkg/registry/core/serviceaccount/registry.go':[ +30, +49, +57, +61, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/controller/storage/storage.go':[ +64, +72, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/daemonset/storage/storage.go':[ +80, +85, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/deployment/registry.go':[ +32, +33, +49, +60, +68, +76, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/deployment/storage/storage.go':[ +113, +118, +134, +155, +167, +173, +203, +215, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/deployment/strategy.go':[ +134, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/ingress/storage/storage.go':[ +79, +84, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/networkpolicy/strategy.go':[ +87, +107, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/podsecuritypolicy/strategy.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/replicaset/registry.go':[ +35, +37, +54, +65, +69, +77, +85, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/replicaset/storage/storage.go':[ +111, +116, +132, +144, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/rest/storage_extensions.go':[ +49, +50, +51, +55, +62, +95, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/rest/thirdparty_controller.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresource/storage/storage.go':[ +57, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresource/strategy.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/codec.go':[ +44, +101, +108, +115, +154, +195, +213, +217, +253, +284, +307, +334, +430, +587, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/registry.go':[ +31, +32, +33, +34, +35, +50, +58, +62, +70, +75, +76, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/storage/storage.go':[ +58, +66, +74, +82, +113, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/strategy.go':[ +72, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/util.go':[ +27, +], +'go-project:sources/go/kubernetes/pkg/registry/networking/networkpolicy/registry.go':[ +31, +34, +50, +69, +73, +], +'go-project:sources/go/kubernetes/pkg/registry/networking/networkpolicy/strategy.go':[ +86, +106, +], +'go-project:sources/go/kubernetes/pkg/registry/networking/rest/storage_settings.go':[ +32, +33, +34, +38, +45, +], +'go-project:sources/go/kubernetes/pkg/registry/policy/poddisruptionbudget/storage/storage.go':[ +76, +81, +], +'go-project:sources/go/kubernetes/pkg/registry/policy/poddisruptionbudget/strategy.go':[ +90, +92, +113, +141, +144, +], +'go-project:sources/go/kubernetes/pkg/registry/policy/rest/storage_policy.go':[ +32, +33, +34, +38, +44, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrole/policybased/storage.go':[ +42, +55, +60, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrole/registry.go':[ +31, +50, +69, +73, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased/storage.go':[ +42, +46, +66, +71, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrolebinding/registry.go':[ +31, +34, +36, +50, +59, +64, +69, +73, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/escalation_check.go':[ +46, +47, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_role.go':[ +68, +162, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go':[ +64, +174, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/rolebinding_interfaces.go':[ +109, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rest/storage_rbac.go':[ +67, +68, +69, +73, +77, +84, +117, +125, +174, +176, +184, +189, +202, +204, +214, +233, +235, +243, +253, +263, +265, +275, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/role/policybased/storage.go':[ +42, +55, +60, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/role/registry.go':[ +50, +69, +92, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rolebinding/policybased/storage.go':[ +41, +45, +51, +72, +77, +79, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rolebinding/registry.go':[ +31, +50, +70, +74, +93, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/policy_compact.go':[ +28, +69, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/policy_comparator.go':[ +29, +65, +69, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/rule.go':[ +34, +38, +39, +43, +49, +60, +67, +79, +120, +173, +227, +], +'go-project:sources/go/kubernetes/pkg/registry/registrytest/endpoint.go':[ +40, +48, +65, +], +'go-project:sources/go/kubernetes/pkg/registry/registrytest/etcd.go':[ +140, +], +'go-project:sources/go/kubernetes/pkg/registry/registrytest/node.go':[ +63, +89, +116, +], +'go-project:sources/go/kubernetes/pkg/registry/registrytest/service.go':[ +51, +90, +117, +124, +], +'go-project:sources/go/kubernetes/pkg/registry/settings/podpreset/registry.go':[ +31, +50, +69, +73, +], +'go-project:sources/go/kubernetes/pkg/registry/settings/rest/storage_settings.go':[ +32, +33, +34, +38, +45, +], +'go-project:sources/go/kubernetes/pkg/registry/storage/rest/storage_storage.go':[ +34, +35, +36, +40, +44, +51, +64, +], +'go-project:sources/go/kubernetes/pkg/registry/storage/storageclass/strategy.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/routes/logs.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/security/apparmor/validate.go':[ +218, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/apparmor/strategy.go':[ +93, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/capabilities/mustrunas.go':[ +38, +85, +121, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/factory.go':[ +42, +75, +169, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/group/mustrunas.go':[ +68, +73, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/provider.go':[ +46, +127, +193, +202, +215, +219, +223, +251, +255, +266, +272, +288, +292, +297, +299, +311, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/seccomp/strategy.go':[ +113, +122, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/selinux/mustrunas.go':[ +61, +67, +71, +75, +79, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/user/mustrunas.go':[ +63, +72, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/user/nonroot.go':[ +56, +60, +61, +], +'go-project:sources/go/kubernetes/pkg/securitycontext/util.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/serviceaccount/jwt.go':[ +89, +184, +290, +294, +305, +306, +], +'go-project:sources/go/kubernetes/pkg/ssh/ssh.go':[ +205, +413, +], +'go-project:sources/go/kubernetes/pkg/util/async/bounded_frequency_runner.go':[ +134, +141, +230, +], +'go-project:sources/go/kubernetes/pkg/util/exec/fake_exec.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/util/goroutinemap/exponentialbackoff/exponential_backoff.go':[ +116, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/iptables.go':[ +196, +199, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/testing/fake.go':[ +65, +88, +], +'go-project:sources/go/kubernetes/pkg/util/mount/mount_linux.go':[ +122, +377, +385, +394, +], +'go-project:sources/go/kubernetes/pkg/util/mount/nsenter_mount.go':[ +192, +], +'go-project:sources/go/kubernetes/pkg/util/netsh/netsh.go':[ +37, +47, +106, +170, +], +'go-project:sources/go/kubernetes/pkg/util/netsh/testing/fake.go':[ +43, +63, +], +'go-project:sources/go/kubernetes/pkg/util/node/node.go':[ +153, +], +'go-project:sources/go/kubernetes/pkg/util/taints/taints.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/util/version/version.go':[ +38, +39, +], +'go-project:sources/go/kubernetes/pkg/version/prometheus/prometheus.go':[ +30, +32, +35, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/attacher.go':[ +80, +99, +145, +185, +201, +262, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/aws_ebs.go':[ +105, +110, +112, +146, +161, +179, +219, +223, +247, +434, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/aws_util.go':[ +68, +81, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/attacher.go':[ +89, +91, +102, +114, +147, +176, +188, +205, +253, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_common.go':[ +127, +139, +151, +330, +338, +367, +377, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_dd.go':[ +32, +35, +39, +52, +169, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_mounter.go':[ +124, +127, +132, +136, +140, +145, +148, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_provision.go':[ +70, +85, +162, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_file/azure_file.go':[ +100, +104, +130, +208, +213, +215, +288, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_file/azure_provision.go':[ +62, +102, +136, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_file/azure_util.go':[ +56, +61, +], +'go-project:sources/go/kubernetes/pkg/volume/cephfs/cephfs.go':[ +68, +116, +159, +266, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/attacher.go':[ +141, +164, +171, +196, +211, +248, +383, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder.go':[ +97, +122, +149, +181, +244, +486, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder_util.go':[ +161, +171, +205, +], +'go-project:sources/go/kubernetes/pkg/volume/configmap/configmap.go':[ +89, +248, +], +'go-project:sources/go/kubernetes/pkg/volume/downwardapi/downwardapi.go':[ +95, +180, +184, +190, +221, +249, +271, +291, +], +'go-project:sources/go/kubernetes/pkg/volume/empty_dir/empty_dir.go':[ +101, +102, +105, +123, +126, +355, +], +'go-project:sources/go/kubernetes/pkg/volume/fc/fc.go':[ +70, +101, +103, +135, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/attacher-defaults.go':[ +40, +50, +51, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/attacher.go':[ +50, +98, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/detacher-defaults.go':[ +43, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/driver-call.go':[ +147, +166, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/plugin.go':[ +165, +170, +197, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/util.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/volume/flocker/flocker.go':[ +139, +144, +172, +288, +293, +313, +457, +], +'go-project:sources/go/kubernetes/pkg/volume/flocker/flocker_util.go':[ +51, +], +'go-project:sources/go/kubernetes/pkg/volume/flocker/flocker_volume.go':[ +31, +59, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/attacher.go':[ +99, +133, +186, +218, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/gce_pd.go':[ +101, +118, +120, +151, +166, +183, +214, +268, +379, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/gce_util.go':[ +80, +], +'go-project:sources/go/kubernetes/pkg/volume/git_repo/git_repo.go':[ +92, +242, +], +'go-project:sources/go/kubernetes/pkg/volume/glusterfs/glusterfs.go':[ +137, +157, +164, +185, +282, +370, +479, +491, +672, +736, +819, +974, +983, +985, +987, +993, +997, +999, +1001, +1025, +], +'go-project:sources/go/kubernetes/pkg/volume/host_path/host_path.go':[ +34, +36, +102, +123, +129, +171, +], +'go-project:sources/go/kubernetes/pkg/volume/iscsi/iscsi.go':[ +74, +118, +120, +161, +], +'go-project:sources/go/kubernetes/pkg/volume/iscsi/iscsi_util.go':[ +49, +57, +69, +77, +97, +134, +136, +227, +234, +237, +], +'go-project:sources/go/kubernetes/pkg/volume/local/local.go':[ +97, +202, +], +'go-project:sources/go/kubernetes/pkg/volume/nfs/nfs.go':[ +36, +38, +111, +135, +152, +188, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/attacher.go':[ +84, +117, +144, +156, +181, +212, +246, +252, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/photon_pd.go':[ +91, +99, +122, +307, +335, +347, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/photon_util.go':[ +83, +110, +], +'go-project:sources/go/kubernetes/pkg/volume/plugins.go':[ +579, +], +'go-project:sources/go/kubernetes/pkg/volume/portworx/portworx.go':[ +93, +97, +142, +160, +205, +355, +], +'go-project:sources/go/kubernetes/pkg/volume/portworx/portworx_util.go':[ +199, +], +'go-project:sources/go/kubernetes/pkg/volume/projected/projected.go':[ +105, +197, +287, +], +'go-project:sources/go/kubernetes/pkg/volume/quobyte/quobyte.go':[ +157, +161, +187, +260, +360, +395, +465, +], +'go-project:sources/go/kubernetes/pkg/volume/quobyte/quobyte_util.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/disk_manager.go':[ +19, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd.go':[ +80, +124, +132, +163, +220, +240, +259, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd_util.go':[ +163, +164, +184, +358, +360, +396, +398, +], +'go-project:sources/go/kubernetes/pkg/volume/scaleio/sio_volume.go':[ +166, +263, +], +'go-project:sources/go/kubernetes/pkg/volume/secret/secret.go':[ +96, +247, +], +'go-project:sources/go/kubernetes/pkg/volume/storageos/storageos.go':[ +69, +107, +117, +148, +199, +220, +423, +435, +465, +557, +], +'go-project:sources/go/kubernetes/pkg/volume/storageos/storageos_util.go':[ +250, +], +'go-project:sources/go/kubernetes/pkg/volume/testing/testing.go':[ +59, +63, +99, +389, +], +'go-project:sources/go/kubernetes/pkg/volume/util.go':[ +57, +63, +132, +421, +433, +449, +], +'go-project:sources/go/kubernetes/pkg/volume/util/nestedpendingoperations/nestedpendingoperations.go':[ +249, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go':[ +65, +70, +74, +81, +95, +104, +116, +138, +160, +531, +631, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go':[ +76, +79, +82, +85, +88, +91, +94, +119, +172, +408, +438, +520, +534, +636, +649, +656, +670, +673, +690, +], +'go-project:sources/go/kubernetes/pkg/volume/util/util.go':[ +154, +], +'go-project:sources/go/kubernetes/pkg/volume/util/volumehelper/volumehelper.go':[ +102, +], +'go-project:sources/go/kubernetes/pkg/volume/validation/pv_validation.go':[ +56, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/attacher.go':[ +87, +121, +151, +205, +237, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume.go':[ +92, +100, +122, +337, +349, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume_util.go':[ +133, +201, +205, +237, +], +'go-project:sources/go/kubernetes/pkg/watch/json/types.go':[ +35, +41, +], +'go-project:sources/go/kubernetes/plugin/cmd/kube-scheduler/app/configurator.go':[ +53, +147, +], +'go-project:sources/go/kubernetes/plugin/cmd/kube-scheduler/app/options/options.go':[ +72, +73, +74, +76, +77, +79, +80, +81, +85, +86, +88, +89, +90, +92, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/antiaffinity/admission.go':[ +48, +67, +71, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/gc/gc_admission.go':[ +107, +117, +125, +168, +174, +182, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/imagepolicy/admission.go':[ +149, +237, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/imagepolicy/config.go':[ +57, +61, +65, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialization/initialization.go':[ +115, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/admission.go':[ +35, +36, +37, +121, +126, +140, +155, +168, +179, +187, +194, +202, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/data_source.go':[ +28, +32, +43, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/gcm.go':[ +65, +87, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/hawkular.go':[ +88, +99, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/influxdb.go':[ +31, +32, +48, +68, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/limitranger/admission.go':[ +61, +105, +108, +203, +262, +276, +282, +285, +288, +295, +300, +303, +309, +315, +318, +321, +327, +333, +336, +348, +482, +500, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/noderestriction/admission.go':[ +131, +136, +141, +155, +176, +195, +234, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/persistentvolume/label/admission.go':[ +56, +87, +94, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podnodeselector/admission.go':[ +138, +151, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podpreset/admission.go':[ +93, +222, +260, +281, +309, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/admission.go':[ +159, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/doc.go':[ +19, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/install/install.go':[ +30, +35, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/doc.go':[ +23, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/admission.go':[ +75, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/install/install.go':[ +30, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/controller.go':[ +43, +108, +197, +199, +201, +202, +205, +206, +207, +208, +209, +290, +293, +305, +333, +367, +368, +460, +462, +468, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/resource_access.go':[ +33, +51, +57, +119, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/security/podsecuritypolicy/admission.go':[ +56, +89, +155, +161, +170, +208, +215, +229, +236, +258, +283, +301, +344, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/securitycontext/scdeny/admission.go':[ +58, +62, +65, +70, +76, +79, +87, +90, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/serviceaccount/admission.go':[ +42, +45, +71, +73, +85, +88, +90, +172, +176, +263, +322, +330, +340, +353, +363, +370, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/webhook/admission.go':[ +126, +137, +149, +202, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/node/graph.go':[ +215, +220, +226, +252, +253, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer.go':[ +37, +52, +99, +123, +129, +134, +161, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go':[ +64, +75, +89, +96, +98, +142, +144, +145, +147, +182, +267, +283, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go':[ +78, +109, +121, +123, +125, +127, +129, +141, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go':[ +93, +122, +132, +148, +151, +167, +168, +171, +199, +200, +203, +226, +229, +282, +310, +327, +352, +371, +385, +412, +413, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go':[ +73, +114, +124, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator.go':[ +29, +42, +55, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/error.go':[ +56, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/metadata.go':[ +39, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +88, +163, +184, +216, +227, +241, +262, +282, +403, +468, +567, +576, +586, +592, +595, +598, +607, +611, +613, +614, +619, +669, +671, +679, +686, +694, +705, +744, +768, +790, +820, +828, +831, +865, +898, +920, +934, +977, +1008, +1010, +1060, +1115, +1235, +1243, +1249, +1268, +1290, +1308, +1317, +1368, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/balanced_resource_allocation.go':[ +51, +80, +107, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/image_locality.go':[ +32, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity.go':[ +86, +107, +110, +117, +119, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/least_requested.go':[ +33, +62, +79, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/most_requested.go':[ +33, +65, +82, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go':[ +35, +53, +78, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_label.go':[ +34, +45, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_prefer_avoid_pods.go':[ +29, +54, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/selector_spreading.go':[ +63, +96, +97, +102, +200, +227, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/taint_toleration.go':[ +44, +55, +56, +76, +77, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/test_util.go':[ +44, +45, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util/topologies.go':[ +80, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/scheduler_interface.go':[ +32, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/types.go':[ +31, +36, +42, +49, +93, +101, +132, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go':[ +102, +192, +197, +202, +208, +222, +225, +228, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/api/types.go':[ +46, +130, +132, +137, +138, +145, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/api/v1/types.go':[ +38, +122, +124, +129, +130, +137, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/api/validation/validation.go':[ +33, +40, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/equivalence_cache.go':[ +69, +90, +98, +101, +162, +182, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/extender.go':[ +100, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/generic_scheduler.go':[ +107, +121, +222, +234, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +97, +104, +400, +404, +428, +644, +647, +651, +734, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/scheduler.go':[ +129, +130, +136, +248, +252, +256, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/schedulercache/node_info.go':[ +257, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/testing/fake_lister.go':[ +108, +137, +165, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/testutil.go':[ +68, +113, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/examples/client-go/client/cr.go':[ +38, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/helpers.go':[ +49, +70, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/install/install.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/types.go':[ +36, +119, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go':[ +26, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/types.go':[ +36, +87, +119, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go':[ +49, +62, +70, +84, +107, +112, +113, +114, +118, +119, +125, +127, +132, +141, +144, +156, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go':[ +118, +127, +132, +141, +152, +181, +182, +189, +194, +198, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery_controller.go':[ +54, +140, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go':[ +180, +188, +196, +252, +335, +381, +385, +393, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/customresourcedefinition.go':[ +44, +60, +61, +71, +72, +86, +118, +119, +131, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/fake/fake_customresourcedefinition.go':[ +34, +36, +38, +40, +47, +49, +56, +58, +78, +87, +89, +114, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/clientset.go':[ +21, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/scheme/register.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion/customresourcedefinition.go':[ +44, +60, +61, +71, +72, +86, +118, +119, +131, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion/fake/fake_apiextensions_client.go':[ +20, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/internalclientset/typed/apiextensions/internalversion/fake/fake_customresourcedefinition.go':[ +34, +36, +38, +40, +47, +49, +56, +58, +67, +78, +87, +89, +114, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/apiextensions/v1beta1/customresourcedefinition.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/factory.go':[ +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/generic.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/apiextensions/internalversion/customresourcedefinition.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/factory.go':[ +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/generic.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/internalversion/customresourcedefinition.go':[ +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1beta1/customresourcedefinition.go':[ +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server/start.go':[ +45, +90, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go':[ +85, +108, +160, +166, +225, +300, +309, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go':[ +72, +76, +89, +123, +207, +249, +250, +255, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd.go':[ +33, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go':[ +43, +62, +65, +80, +89, +105, +128, +131, +134, +137, +151, +154, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go':[ +70, +91, +171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/strategy.go':[ +70, +107, +115, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go':[ +37, +55, +93, +111, +144, +152, +167, +175, +179, +180, +252, +257, +275, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/start.go':[ +63, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go':[ +251, +256, +258, +289, +426, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/errors.go':[ +36, +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/priority.go':[ +55, +56, +88, +92, +124, +216, +220, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go':[ +123, +135, +143, +145, +201, +526, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/codec.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/fuzzer.go':[ +163, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go':[ +45, +47, +65, +76, +103, +107, +111, +130, +134, +138, +181, +218, +306, +313, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go':[ +70, +82, +95, +118, +133, +134, +146, +147, +237, +266, +288, +289, +291, +292, +297, +302, +307, +310, +313, +315, +316, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/validation/path/name.go':[ +24, +27, +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apimachinery/announced/announced.go':[ +78, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apimachinery/announced/group_factory.go':[ +130, +156, +159, +167, +243, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apimachinery/registered/registered.go':[ +331, +333, +345, +346, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apimachinery/types.go':[ +86, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/conversion.go':[ +27, +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go':[ +228, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go':[ +85, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go':[ +191, +214, +263, +401, +673, +674, +696, +703, +704, +707, +814, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go':[ +585, +738, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation.go':[ +44, +48, +81, +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1alpha1/types.go':[ +138, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +406, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/queryparams/convert.go':[ +97, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/fields/selector.go':[ +329, +366, +367, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +201, +209, +213, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/openapi/common.go':[ +38, +67, +70, +90, +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/codec.go':[ +88, +132, +213, +247, +248, +263, +301, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/schema/group_version.go':[ +24, +25, +26, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go':[ +99, +102, +155, +194, +395, +403, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/codec_factory.go':[ +166, +194, +197, +224, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go':[ +35, +45, +46, +69, +70, +71, +72, +73, +74, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go':[ +50, +58, +59, +82, +83, +84, +85, +86, +88, +290, +291, +295, +303, +313, +314, +315, +316, +317, +319, +389, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/recognizer/recognizer.go':[ +84, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go':[ +74, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go':[ +36, +48, +93, +94, +96, +265, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/yaml/yaml.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/swagger_doc_generator.go':[ +78, +100, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/types.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/cache/lruexpirecache.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go':[ +212, +215, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go':[ +143, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go':[ +293, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go':[ +73, +76, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/jsonmergepatch/patch.go':[ +31, +67, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/http.go':[ +86, +258, +310, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/remotecommand/constants.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +94, +120, +154, +180, +226, +312, +392, +536, +611, +658, +667, +693, +719, +726, +734, +759, +772, +963, +966, +994, +1004, +1010, +1166, +1177, +1195, +1196, +1228, +1255, +1313, +1360, +1415, +1471, +1519, +1610, +1958, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go':[ +30, +55, +71, +91, +110, +129, +152, +170, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/wait/wait.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/json/fields.go':[ +89, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/attributes.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/chain.go':[ +19, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/initializer/initializer.go':[ +33, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/interfaces.go':[ +38, +39, +40, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go':[ +67, +90, +91, +158, +182, +222, +223, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/plugins.go':[ +121, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/apiserver/install/install.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/audit/install/install.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/audit/validation/validation.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/install/install.go':[ +30, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/types.go':[ +117, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/v1/types.go':[ +62, +115, +124, +129, +171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/format.go':[ +63, +64, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/request.go':[ +42, +109, +189, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/authenticatorfactory/requestheader.go':[ +20, +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/group/authenticated_group_adder.go':[ +33, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/group/group_adder.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/request/headerrequest/requestheader.go':[ +34, +37, +79, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/request/union/union.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/request/x509/x509.go':[ +124, +134, +135, +172, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/token/tokenfile/tokenfile.go':[ +63, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authorization/authorizer/interfaces.go':[ +31, +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authorization/union/union.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/group.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/legacy.go':[ +44, +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/root.go':[ +62, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/util.go':[ +63, +66, +67, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/discovery/version.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go':[ +40, +136, +180, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go':[ +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go':[ +37, +38, +63, +66, +107, +158, +168, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/legacy_audit.go':[ +99, +139, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/requestinfo.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/errors.go':[ +59, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate.go':[ +43, +62, +63, +107, +204, +268, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go':[ +88, +122, +217, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/response.go':[ +36, +52, +77, +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go':[ +42, +54, +89, +100, +121, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +109, +157, +167, +196, +197, +235, +258, +289, +312, +343, +362, +401, +407, +433, +451, +463, +511, +516, +524, +530, +577, +583, +714, +799, +800, +814, +830, +847, +853, +878, +886, +902, +904, +942, +948, +975, +989, +1001, +1062, +1064, +1079, +1087, +1121, +1211, +1218, +1221, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go':[ +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +163, +191, +355, +417, +419, +433, +437, +470, +472, +485, +489, +539, +623, +655, +671, +712, +733, +752, +771, +789, +790, +791, +792, +793, +794, +795, +802, +806, +863, +908, +909, +1020, +1026, +1032, +1038, +1044, +1050, +1056, +1068, +1074, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go':[ +40, +110, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/openapi/openapi.go':[ +36, +61, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/context.go':[ +109, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestcontext.go':[ +32, +34, +55, +77, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go':[ +33, +43, +44, +45, +47, +66, +75, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +177, +257, +277, +382, +418, +437, +463, +482, +540, +632, +680, +726, +786, +822, +866, +908, +956, +1066, +1086, +1248, +1292, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/rest/proxy.go':[ +49, +76, +78, +239, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/rest/response_checker.go':[ +62, +64, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/rest/streamer.go':[ +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/create.go':[ +101, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/delete.go':[ +63, +64, +65, +74, +75, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go':[ +63, +69, +75, +127, +161, +174, +198, +265, +266, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go':[ +171, +474, +514, +534, +555, +588, +599, +630, +660, +676, +711, +742, +760, +812, +817, +839, +865, +873, +883, +899, +917, +929, +941, +949, +959, +983, +984, +1014, +1027, +1042, +1055, +1270, +1319, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/table.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go':[ +74, +75, +124, +142, +166, +172, +216, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/config.go':[ +143, +145, +233, +350, +390, +454, +470, +474, +477, +478, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/cors.go':[ +36, +57, +82, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/longrunning.go':[ +26, +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/timeout.go':[ +36, +51, +57, +70, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go':[ +357, +401, +406, +417, +442, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/handler.go':[ +57, +58, +59, +60, +61, +62, +64, +65, +66, +76, +88, +182, +185, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/httplog/log.go':[ +163, +165, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/mux/pathrecorder.go':[ +218, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi.go':[ +151, +250, +361, +381, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi_handler.go':[ +47, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/util.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/authentication.go':[ +67, +182, +222, +240, +259, +289, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/authorization.go':[ +101, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go':[ +43, +98, +103, +113, +122, +124, +130, +138, +164, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/etcd.go':[ +93, +114, +154, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/server_run_options.go':[ +72, +115, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/serving.go':[ +89, +213, +244, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config.go':[ +65, +113, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/resource_encoding_config.go':[ +54, +61, +71, +103, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/storage_codec.go':[ +43, +52, +65, +82, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/storage_factory.go':[ +104, +159, +188, +195, +208, +217, +274, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +143, +294, +343, +351, +356, +401, +457, +694, +769, +850, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_helper.go':[ +42, +45, +46, +65, +165, +168, +175, +233, +248, +257, +263, +272, +274, +293, +332, +422, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_watcher.go':[ +65, +225, +234, +362, +388, +401, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go':[ +92, +96, +123, +187, +222, +373, +403, +441, +446, +450, +459, +568, +593, +596, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go':[ +52, +85, +101, +110, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd2.go':[ +42, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/util.go':[ +121, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/value/transformer.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/watch_cache.go':[ +217, +310, +321, +331, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/flag/string_flag.go':[ +19, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/proxy.go':[ +36, +40, +44, +57, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/trace/trace.go':[ +57, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook.go':[ +41, +57, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/wsstream/conn.go':[ +90, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/password/keystone/keystone.go':[ +42, +60, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile/passwordfile.go':[ +62, +73, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testing/provider.go':[ +181, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/webhook/webhook.go':[ +52, +66, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authorizer/webhook/webhook.go':[ +55, +88, +226, +238, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/discovery_client.go':[ +41, +161, +256, +385, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/helper.go':[ +37, +80, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/restmapper.go':[ +76, +180, +218, +233, +248, +263, +278, +294, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/unstructured.go':[ +68, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/dynamic/client.go':[ +229, +287, +296, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/dynamic/client_pool.go':[ +62, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/dynamic/dynamic_util.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/examples/out-of-cluster-client-configuration/main.go':[ +38, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/examples/workqueue/main.go':[ +43, +165, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/admissionregistration/v1alpha1/externaladmissionhookconfiguration.go':[ +44, +63, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/admissionregistration/v1alpha1/initializerconfiguration.go':[ +44, +63, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/autoscaling/v1/horizontalpodautoscaler.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/autoscaling/v2alpha1/horizontalpodautoscaler.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/certificates/v1beta1/certificatesigningrequest.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/core/v1/persistentvolumeclaim.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/core/v1/replicationcontroller.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/factory.go':[ +102, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/generic.go':[ +71, +73, +77, +79, +81, +85, +89, +101, +105, +119, +121, +127, +139, +141, +143, +145, +147, +149, +153, +157, +161, +163, +167, +171, +173, +177, +181, +185, +189, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/clientset.go':[ +125, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/admissionregistration_client.go':[ +32, +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/externaladmissionhookconfiguration.go':[ +43, +53, +59, +60, +70, +71, +93, +102, +103, +114, +115, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake/fake_admissionregistration_client.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake/fake_externaladmissionhookconfiguration.go':[ +34, +36, +38, +40, +47, +49, +58, +62, +69, +71, +78, +80, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/fake/fake_initializerconfiguration.go':[ +34, +36, +38, +40, +47, +49, +69, +78, +80, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/admissionregistration/v1alpha1/initializerconfiguration.go':[ +43, +59, +60, +70, +71, +102, +103, +115, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/apps/v1beta1/controllerrevision.go':[ +43, +61, +62, +73, +74, +108, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/apps/v1beta1/deployment.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/apps/v1beta1/fake/fake_controllerrevision.go':[ +35, +39, +49, +73, +85, +112, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/apps/v1beta1/fake/fake_deployment.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/apps/v1beta1/fake/fake_statefulset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/apps/v1beta1/statefulset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authentication/v1/fake/fake_tokenreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authentication/v1/tokenreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/fake/fake_tokenreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authentication/v1beta1/tokenreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1/fake/fake_localsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1/fake/fake_selfsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1/fake/fake_subjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1/localsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1/selfsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1/subjectaccessreview_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/fake/fake_authorization_client.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/fake/fake_localsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/fake/fake_selfsubjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/fake/fake_subjectaccessreview_expansion.go':[ +24, +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/localsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/selfsubjectaccessreview_expansion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/authorization/v1beta1/subjectaccessreview_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/autoscaling/v1/fake/fake_horizontalpodautoscaler.go':[ +35, +37, +39, +41, +49, +51, +59, +61, +76, +83, +93, +95, +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/autoscaling/v1/horizontalpodautoscaler.go':[ +44, +62, +63, +74, +75, +90, +115, +125, +126, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1/fake/fake_horizontalpodautoscaler.go':[ +35, +37, +39, +41, +49, +51, +59, +61, +83, +93, +95, +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/autoscaling/v2alpha1/horizontalpodautoscaler.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/batch/v1/fake/fake_job.go':[ +122, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/batch/v1/job.go':[ +62, +74, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/batch/v2alpha1/cronjob.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/batch/v2alpha1/fake/fake_cronjob.go':[ +122, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/certificatesigningrequest.go':[ +44, +60, +61, +71, +72, +86, +118, +119, +131, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/certificatesigningrequest_expansion.go':[ +24, +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake/fake_certificatesigningrequest.go':[ +34, +36, +38, +40, +47, +49, +56, +58, +78, +87, +89, +114, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/certificates/v1beta1/fake/fake_certificatesigningrequest_expansion.go':[ +24, +26, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/componentstatus.go':[ +59, +70, +102, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/configmap.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/endpoints.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/event.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/event_expansion.go':[ +41, +126, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_componentstatus.go':[ +62, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_configmap.go':[ +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_endpoints.go':[ +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_event.go':[ +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_event_expansion.go':[ +82, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_limitrange.go':[ +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_namespace.go':[ +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_node.go':[ +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_persistentvolume.go':[ +58, +71, +114, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_persistentvolumeclaim.go':[ +35, +39, +41, +49, +51, +59, +61, +76, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_pod.go':[ +122, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_podtemplate.go':[ +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_replicationcontroller.go':[ +35, +39, +41, +49, +51, +59, +61, +76, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_resourcequota.go':[ +61, +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_secret.go':[ +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_service.go':[ +122, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/fake/fake_serviceaccount.go':[ +112, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/limitrange.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/namespace.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/namespace_expansion.go':[ +26, +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/node.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/persistentvolume.go':[ +60, +71, +86, +118, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/persistentvolumeclaim.go':[ +44, +62, +63, +74, +75, +90, +115, +125, +126, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/pod.go':[ +62, +74, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/pod_expansion.go':[ +35, +39, +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/podtemplate.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/replicationcontroller.go':[ +44, +62, +63, +74, +75, +90, +115, +125, +126, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/resourcequota.go':[ +62, +74, +125, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/secret.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/service.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/serviceaccount.go':[ +61, +73, +108, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/daemonset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/deployment.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/deployment_expansion.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake/fake_daemonset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake/fake_deployment.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake/fake_ingress.go':[ +122, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake/fake_podsecuritypolicy.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake/fake_replicaset.go':[ +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/fake/fake_thirdpartyresource.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/ingress.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/podsecuritypolicy.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/replicaset.go':[ +62, +74, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/thirdpartyresource.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/networking/v1/fake/fake_networkpolicy.go':[ +35, +112, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/networking/v1/networkpolicy.go':[ +61, +73, +108, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/policy/v1beta1/fake/fake_poddisruptionbudget.go':[ +35, +39, +41, +49, +51, +59, +61, +83, +95, +122, +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/policy/v1beta1/poddisruptionbudget.go':[ +44, +62, +63, +74, +75, +90, +125, +126, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/clusterrole.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/clusterrolebinding.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake/fake_clusterrole.go':[ +34, +36, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake/fake_clusterrolebinding.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake/fake_role.go':[ +35, +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/fake/fake_rolebinding.go':[ +35, +37, +112, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/role.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1alpha1/rolebinding.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/clusterrole.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/clusterrolebinding.go':[ +43, +59, +60, +70, +71, +102, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/fake/fake_clusterrole.go':[ +34, +36, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/fake/fake_clusterrolebinding.go':[ +34, +36, +38, +47, +69, +80, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/fake/fake_role.go':[ +35, +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/fake/fake_rolebinding.go':[ +35, +37, +112, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/role.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/rbac/v1beta1/rolebinding.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/settings/v1alpha1/fake/fake_podpreset.go':[ +35, +112, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/settings/v1alpha1/podpreset.go':[ +61, +73, +144, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/storage/v1/fake/fake_storageclass.go':[ +34, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/storage/v1/storageclass.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/storage/v1beta1/fake/fake_storageclass.go':[ +34, +105, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/storage/v1beta1/storageclass.go':[ +59, +70, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/admissionregistration/v1alpha1/externaladmissionhookconfiguration.go':[ +48, +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/admissionregistration/v1alpha1/initializerconfiguration.go':[ +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/apps/v1beta1/controllerrevision.go':[ +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/apps/v1beta1/statefulset_expansion.go':[ +73, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/autoscaling/v1/horizontalpodautoscaler.go':[ +56, +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/autoscaling/v2alpha1/horizontalpodautoscaler.go':[ +48, +56, +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/batch/v1/job_expansion.go':[ +61, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/certificates/v1beta1/certificatesigningrequest.go':[ +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/core/v1/persistentvolumeclaim.go':[ +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/core/v1/replicationcontroller.go':[ +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/core/v1/replicationcontroller_expansion.go':[ +62, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/extensions/v1beta1/daemonset_expansion.go':[ +76, +110, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/extensions/v1beta1/deployment_expansion.go':[ +66, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/extensions/v1beta1/replicaset_expansion.go':[ +69, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/policy/v1beta1/poddisruptionbudget.go':[ +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/policy/v1beta1/poddisruptionbudget_expansion.go':[ +39, +70, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/annotation_key_constants.go':[ +89, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/register.go':[ +30, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/toleration.go':[ +22, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/types.go':[ +197, +205, +284, +367, +463, +469, +481, +1009, +1120, +1561, +1563, +1922, +1995, +2189, +2198, +2759, +3100, +3228, +3732, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/annotation_key_constants.go':[ +89, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +261, +266, +272, +285, +294, +306, +312, +327, +336, +351, +368, +396, +433, +462, +676, +692, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/resource/helpers.go':[ +30, +106, +120, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/toleration.go':[ +19, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/types.go':[ +179, +202, +213, +233, +270, +275, +300, +317, +334, +342, +377, +382, +417, +430, +438, +499, +505, +517, +528, +534, +606, +627, +631, +634, +1175, +1185, +1478, +1665, +1667, +1702, +1708, +1712, +1732, +1742, +1761, +1786, +1811, +1820, +1827, +2142, +2158, +2167, +2178, +2192, +2201, +2212, +2215, +2257, +2265, +2276, +2344, +2397, +2413, +2420, +2426, +2435, +2440, +2444, +2464, +2491, +2498, +2503, +2522, +2602, +2628, +2742, +2777, +2800, +2816, +2949, +3003, +3018, +3033, +3142, +3149, +3151, +3154, +3349, +3353, +3364, +3371, +3537, +3679, +3687, +4085, +4088, +4089, +4091, +4287, +4428, +4464, +4472, +4526, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/admissionregistration/v1alpha1/types.go':[ +42, +78, +137, +172, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go':[ +87, +114, +128, +162, +168, +179, +191, +210, +237, +245, +264, +270, +292, +296, +305, +309, +318, +328, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.go':[ +45, +69, +73, +121, +123, +182, +200, +316, +326, +428, +459, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authentication/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authorization/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authorization/types.go':[ +78, +97, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authorization/v1/types.go':[ +85, +111, +126, +129, +158, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authorization/v1beta1/types.go':[ +85, +111, +126, +129, +158, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/types.go':[ +32, +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/conversion.go':[ +45, +52, +74, +116, +150, +173, +181, +191, +192, +200, +212, +213, +236, +244, +255, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/types.go':[ +49, +72, +84, +111, +115, +223, +228, +326, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v2alpha1/types.go':[ +140, +145, +270, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/v1/types.go':[ +80, +113, +155, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/v2alpha1/types.go':[ +95, +100, +113, +118, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/certificates/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/install/install.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/types.go':[ +73, +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/conversion.go':[ +85, +104, +131, +137, +159, +167, +186, +190, +199, +203, +212, +228, +238, +248, +255, +266, +278, +283, +288, +294, +301, +307, +314, +317, +324, +332, +335, +342, +350, +370, +390, +401, +412, +423, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.go':[ +39, +63, +67, +216, +226, +328, +359, +493, +785, +812, +835, +853, +891, +895, +900, +922, +931, +1048, +1133, +1158, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/networking/v1/conversion.go':[ +45, +50, +55, +61, +68, +74, +81, +99, +117, +137, +157, +168, +179, +190, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/networking/v1/types.go':[ +86, +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/policy/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/helpers.go':[ +214, +269, +276, +283, +366, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/types.go':[ +45, +49, +53, +56, +57, +58, +59, +63, +75, +104, +105, +142, +155, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1alpha1/helpers.go':[ +74, +127, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1alpha1/types.go':[ +50, +53, +60, +64, +65, +66, +67, +72, +86, +117, +118, +159, +173, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1beta1/helpers.go':[ +74, +127, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1beta1/types.go':[ +50, +53, +60, +64, +65, +66, +71, +84, +115, +116, +157, +171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/settings/install/install.go':[ +35, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/storage/install/install.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/plugin/pkg/auth/authenticator/token/oidc/testing/provider.go':[ +181, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/plugin/pkg/client/auth/azure/azure.go':[ +78, +159, +309, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/plugin/pkg/client/auth/gcp/gcp.go':[ +97, +141, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/plugin/pkg/client/auth/oidc/oidc.go':[ +109, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/client.go':[ +94, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/config.go':[ +246, +312, +394, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/plugin.go':[ +42, +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +122, +327, +338, +583, +651, +717, +718, +787, +934, +1027, +1028, +1206, +1209, +1230, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/url_utils.go':[ +30, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/actions.go':[ +50, +61, +132, +143, +155, +164, +204, +225, +261, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/fixture.go':[ +150, +299, +398, +424, +445, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/exec.go':[ +45, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go':[ +518, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/expiration_cache_fakes.go':[ +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/mutation_cache.go':[ +62, +89, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/reflector.go':[ +83, +100, +340, +376, +381, +389, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/shared_informer.go':[ +77, +92, +274, +286, +292, +298, +310, +467, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/types.go':[ +24, +47, +56, +63, +67, +76, +81, +83, +100, +121, +126, +128, +137, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/types.go':[ +24, +46, +54, +63, +72, +77, +94, +115, +120, +129, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/auth_loaders.go':[ +98, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go':[ +77, +90, +93, +94, +98, +99, +168, +184, +188, +190, +203, +207, +210, +269, +280, +328, +329, +442, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/config.go':[ +37, +39, +41, +43, +150, +151, +152, +153, +154, +173, +192, +353, +390, +452, +464, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/helpers.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/loader.go':[ +111, +114, +119, +123, +135, +394, +401, +459, +492, +513, +577, +578, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/merged_client_builder.go':[ +55, +58, +59, +60, +76, +78, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/overrides.go':[ +27, +39, +75, +76, +151, +159, +163, +170, +176, +181, +185, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/validation.go':[ +132, +133, +159, +160, +184, +190, +213, +217, +221, +228, +235, +240, +242, +246, +247, +252, +259, +265, +270, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/portforward/portforward.go':[ +114, +293, +302, +315, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/event.go':[ +125, +171, +215, +257, +260, +295, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/events_cache.go':[ +76, +116, +311, +312, +313, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/fake.go':[ +45, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/remotecommand.go':[ +99, +171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/transport/cache.go':[ +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/transport/round_trippers.go':[ +100, +405, +408, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/transport/transport.go':[ +131, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/cert/cert.go':[ +86, +182, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/cert/csr.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/cert/io.go':[ +111, +125, +138, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/cert/triple/triple.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/testing/fake_handler.go':[ +126, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/install/install.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/types.go':[ +56, +57, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1/types.go':[ +56, +57, +117, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go':[ +49, +62, +65, +70, +73, +85, +91, +92, +105, +106, +113, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go':[ +179, +190, +210, +230, +238, +243, +247, +340, +393, +426, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go':[ +62, +69, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go':[ +134, +139, +154, +215, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1beta1/apiservice.go':[ +60, +71, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1beta1/fake/fake_apiservice.go':[ +34, +114, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset/clientset.go':[ +24, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset/scheme/register.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset/typed/apiregistration/internalversion/apiservice.go':[ +44, +60, +71, +86, +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset/typed/apiregistration/internalversion/fake/fake_apiregistration_client.go':[ +22, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/internalclientset/typed/apiregistration/internalversion/fake/fake_apiservice.go':[ +34, +38, +47, +58, +114, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/externalversions/factory.go':[ +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/externalversions/generic.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/internalversion/factory.go':[ +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/internalversion/generic.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/cmd/server/start.go':[ +83, +86, +87, +93, +96, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/controllers/autoregister/autoregister_controller.go':[ +36, +45, +47, +89, +207, +208, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go':[ +40, +81, +138, +145, +156, +163, +178, +201, +291, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go':[ +76, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/strategy.go':[ +126, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/apis/custom_metrics/install/install.go':[ +30, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/apis/metrics/install/install.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/apis/metrics/v1alpha1/types.go':[ +40, +88, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/client/custom_metrics/client.go':[ +131, +137, +161, +167, +199, +219, +225, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/client/custom_metrics/fake/fake_client.go':[ +54, +73, +114, +124, +130, +145, +155, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/client/custom_metrics/interfaces.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/apis/wardle/install/install.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/apiserver/apiserver.go':[ +93, +102, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go':[ +43, +89, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/strategy.go':[ +79, +], +'go-project:sources/go/kubernetes/test/e2e/addon_update.go':[ +248, +273, +282, +288, +292, +293, +294, +307, +308, +309, +314, +339, +343, +346, +347, +350, +351, +], +'go-project:sources/go/kubernetes/test/e2e/api/table_conversion.go':[ +49, +69, +87, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/autoscaling_timer.go':[ +82, +92, +94, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/cluster_size_autoscaling.go':[ +140, +166, +171, +196, +227, +236, +343, +391, +396, +417, +425, +434, +450, +479, +499, +509, +570, +618, +697, +717, +742, +896, +943, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/dns_autoscaling.go':[ +112, +327, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/horizontal_pod_autoscaling.go':[ +115, +118, +120, +], +'go-project:sources/go/kubernetes/test/e2e/cadvisor.go':[ +86, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/sd_utils.go':[ +131, +140, +142, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/utils.go':[ +92, +103, +], +'go-project:sources/go/kubernetes/test/e2e/cluster_upgrade.go':[ +171, +278, +], +'go-project:sources/go/kubernetes/test/e2e/common/autoscaling_utils.go':[ +102, +103, +108, +109, +120, +121, +123, +234, +262, +290, +319, +326, +333, +401, +403, +404, +420, +501, +511, +], +'go-project:sources/go/kubernetes/test/e2e/common/configmap.go':[ +45, +71, +125, +262, +274, +286, +305, +310, +319, +326, +337, +], +'go-project:sources/go/kubernetes/test/e2e/common/container_probe.go':[ +240, +], +'go-project:sources/go/kubernetes/test/e2e/common/downwardapi_volume.go':[ +192, +199, +319, +417, +], +'go-project:sources/go/kubernetes/test/e2e/common/events.go':[ +41, +123, +], +'go-project:sources/go/kubernetes/test/e2e/common/host_path.go':[ +134, +178, +], +'go-project:sources/go/kubernetes/test/e2e/common/networking.go':[ +36, +43, +50, +57, +], +'go-project:sources/go/kubernetes/test/e2e/common/pods.go':[ +74, +113, +116, +231, +607, +], +'go-project:sources/go/kubernetes/test/e2e/common/projected.go':[ +39, +47, +63, +306, +318, +330, +349, +354, +381, +394, +420, +479, +634, +646, +658, +677, +682, +691, +698, +709, +955, +962, +1391, +1398, +1482, +1492, +1502, +], +'go-project:sources/go/kubernetes/test/e2e/common/secrets.go':[ +46, +62, +265, +277, +289, +308, +313, +340, +], +'go-project:sources/go/kubernetes/test/e2e/cronjob.go':[ +49, +50, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_restart.go':[ +70, +312, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_set.go':[ +84, +168, +394, +407, +412, +426, +431, +491, +505, +517, +519, +668, +677, +713, +718, +743, +752, +761, +803, +816, +829, +], +'go-project:sources/go/kubernetes/test/e2e/dashboard.go':[ +47, +], +'go-project:sources/go/kubernetes/test/e2e/deployment.go':[ +131, +189, +190, +262, +263, +309, +314, +331, +332, +342, +426, +467, +497, +538, +634, +722, +723, +754, +881, +902, +923, +973, +1011, +1040, +1071, +1100, +1126, +1138, +1165, +1168, +1176, +1191, +1219, +1265, +1272, +1321, +1324, +1343, +1363, +1374, +1384, +1431, +], +'go-project:sources/go/kubernetes/test/e2e/dns.go':[ +114, +127, +130, +141, +142, +143, +151, +152, +172, +378, +379, +], +'go-project:sources/go/kubernetes/test/e2e/dns_common.go':[ +78, +], +'go-project:sources/go/kubernetes/test/e2e/e2e.go':[ +184, +191, +357, +399, +], +'go-project:sources/go/kubernetes/test/e2e/empty.go':[ +46, +], +'go-project:sources/go/kubernetes/test/e2e/empty_dir_wrapper.go':[ +226, +311, +], +'go-project:sources/go/kubernetes/test/e2e/example_cluster_dns.go':[ +131, +134, +150, +], +'go-project:sources/go/kubernetes/test/e2e/examples.go':[ +187, +195, +216, +250, +276, +316, +489, +491, +], +'go-project:sources/go/kubernetes/test/e2e/extension/initializers.go':[ +132, +138, +], +'go-project:sources/go/kubernetes/test/e2e/firewall.go':[ +69, +80, +99, +127, +141, +145, +161, +171, +177, +], +'go-project:sources/go/kubernetes/test/e2e/framework/authorizer_util.go':[ +40, +94, +], +'go-project:sources/go/kubernetes/test/e2e/framework/exec_util.go':[ +87, +137, +], +'go-project:sources/go/kubernetes/test/e2e/framework/firewall_util.go':[ +80, +85, +353, +], +'go-project:sources/go/kubernetes/test/e2e/framework/framework.go':[ +213, +246, +281, +314, +406, +450, +455, +460, +499, +500, +581, +586, +], +'go-project:sources/go/kubernetes/test/e2e/framework/get-kubemark-resource-usage.go':[ +32, +57, +80, +], +'go-project:sources/go/kubernetes/test/e2e/framework/google_compute.go':[ +39, +40, +50, +82, +83, +91, +], +'go-project:sources/go/kubernetes/test/e2e/framework/ingress_utils.go':[ +204, +265, +297, +376, +784, +938, +], +'go-project:sources/go/kubernetes/test/e2e/framework/jobs_util.go':[ +106, +], +'go-project:sources/go/kubernetes/test/e2e/framework/kubelet_stats.go':[ +189, +262, +504, +545, +557, +578, +692, +830, +], +'go-project:sources/go/kubernetes/test/e2e/framework/metrics_util.go':[ +243, +560, +], +'go-project:sources/go/kubernetes/test/e2e/framework/networking_utils.go':[ +129, +130, +133, +134, +174, +217, +230, +266, +273, +297, +566, +571, +639, +643, +790, +794, +], +'go-project:sources/go/kubernetes/test/e2e/framework/perf_util.go':[ +100, +], +'go-project:sources/go/kubernetes/test/e2e/framework/pods.go':[ +80, +140, +142, +180, +], +'go-project:sources/go/kubernetes/test/e2e/framework/pv_util.go':[ +174, +217, +280, +315, +349, +796, +834, +842, +867, +871, +], +'go-project:sources/go/kubernetes/test/e2e/framework/resource_usage_gatherer.go':[ +77, +159, +219, +278, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +140, +222, +247, +485, +575, +591, +612, +716, +765, +771, +777, +783, +833, +848, +1005, +1043, +1097, +1119, +1131, +1135, +1197, +1211, +1268, +1273, +1341, +], +'go-project:sources/go/kubernetes/test/e2e/framework/statefulset_utils.go':[ +125, +148, +152, +161, +166, +204, +235, +238, +329, +342, +616, +709, +718, +756, +788, +], +'go-project:sources/go/kubernetes/test/e2e/framework/test_context.go':[ +172, +173, +174, +175, +176, +177, +178, +179, +180, +181, +183, +184, +185, +186, +187, +188, +189, +190, +191, +196, +197, +198, +199, +200, +201, +203, +204, +205, +206, +207, +208, +210, +211, +215, +219, +221, +224, +225, +226, +227, +228, +229, +230, +231, +232, +233, +235, +236, +237, +238, +252, +253, +254, +257, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +493, +500, +525, +571, +632, +663, +675, +694, +706, +782, +791, +818, +850, +851, +870, +890, +891, +1036, +1042, +1045, +1069, +1083, +1086, +1162, +1297, +1323, +1386, +1391, +1399, +1400, +1403, +1417, +1422, +1457, +1481, +1514, +1533, +1539, +1565, +1583, +1584, +1602, +1636, +1650, +1684, +1687, +1772, +1780, +1839, +1925, +1957, +1978, +2000, +2014, +2017, +2027, +2029, +2034, +2042, +2048, +2069, +2177, +2192, +2198, +2441, +2767, +2771, +2793, +2828, +2869, +2937, +2990, +2996, +3111, +3174, +3205, +3206, +3248, +3260, +3261, +3267, +3273, +3305, +3332, +3333, +3340, +3394, +3405, +3417, +3437, +3458, +3483, +3508, +3533, +3557, +3581, +3840, +3925, +3930, +4054, +4498, +4505, +4512, +4587, +4635, +4764, +5121, +5354, +5402, +5425, +5441, +], +'go-project:sources/go/kubernetes/test/e2e/framework/volume_util.go':[ +313, +319, +], +'go-project:sources/go/kubernetes/test/e2e/garbage_collector.go':[ +489, +567, +606, +689, +], +'go-project:sources/go/kubernetes/test/e2e/generated/gobindata_util.go':[ +31, +], +'go-project:sources/go/kubernetes/test/e2e/gke_node_pools.go':[ +60, +85, +], +'go-project:sources/go/kubernetes/test/e2e/ha_master.go':[ +34, +44, +54, +64, +84, +172, +], +'go-project:sources/go/kubernetes/test/e2e/ingress.go':[ +118, +121, +160, +162, +170, +], +'go-project:sources/go/kubernetes/test/e2e/job.go':[ +81, +], +'go-project:sources/go/kubernetes/test/e2e/kubectl.go':[ +145, +168, +223, +280, +288, +290, +291, +293, +294, +300, +302, +355, +365, +410, +421, +425, +445, +449, +455, +461, +465, +468, +481, +489, +497, +502, +617, +618, +627, +630, +633, +636, +659, +662, +702, +708, +726, +736, +899, +950, +955, +1024, +1058, +1139, +1179, +1231, +1281, +1371, +1415, +1619, +1787, +1862, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet.go':[ +74, +276, +337, +423, +427, +446, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet_perf.go':[ +64, +123, +204, +205, +], +'go-project:sources/go/kubernetes/test/e2e/limit_range.go':[ +52, +84, +], +'go-project:sources/go/kubernetes/test/e2e/logging_soak.go':[ +40, +42, +65, +85, +86, +130, +], +'go-project:sources/go/kubernetes/test/e2e/monitoring.go':[ +68, +], +'go-project:sources/go/kubernetes/test/e2e/network_partition.go':[ +112, +154, +207, +216, +262, +327, +398, +445, +465, +486, +587, +], +'go-project:sources/go/kubernetes/test/e2e/network_policy.go':[ +362, +437, +454, +], +'go-project:sources/go/kubernetes/test/e2e/networking.go':[ +96, +97, +99, +100, +105, +106, +108, +109, +114, +115, +123, +124, +132, +133, +135, +136, +141, +142, +144, +145, +150, +151, +155, +156, +161, +162, +166, +167, +], +'go-project:sources/go/kubernetes/test/e2e/networking_perf.go':[ +51, +152, +], +'go-project:sources/go/kubernetes/test/e2e/nodeoutofdisk.go':[ +268, +], +'go-project:sources/go/kubernetes/test/e2e/nvidia-gpus.go':[ +46, +143, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +171, +223, +228, +431, +438, +457, +660, +735, +736, +738, +817, +], +'go-project:sources/go/kubernetes/test/e2e/perf/load.go':[ +154, +171, +174, +192, +371, +375, +379, +547, +], +'go-project:sources/go/kubernetes/test/e2e/podpreset.go':[ +149, +255, +], +'go-project:sources/go/kubernetes/test/e2e/pods.go':[ +129, +160, +], +'go-project:sources/go/kubernetes/test/e2e/portforward.go':[ +115, +], +'go-project:sources/go/kubernetes/test/e2e/proxy.go':[ +62, +66, +67, +68, +158, +161, +164, +167, +203, +204, +210, +231, +], +'go-project:sources/go/kubernetes/test/e2e/rc.go':[ +64, +124, +144, +], +'go-project:sources/go/kubernetes/test/e2e/reboot.go':[ +113, +], +'go-project:sources/go/kubernetes/test/e2e/replica_set.go':[ +38, +136, +156, +], +'go-project:sources/go/kubernetes/test/e2e/resize_nodes.go':[ +66, +97, +144, +], +'go-project:sources/go/kubernetes/test/e2e/resource_quota.go':[ +323, +374, +385, +477, +487, +701, +], +'go-project:sources/go/kubernetes/test/e2e/restart.go':[ +88, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/opaque_resource.go':[ +134, +175, +267, +268, +273, +287, +293, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/predicates.go':[ +97, +128, +421, +673, +683, +977, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/priorities.go':[ +83, +258, +347, +365, +366, +396, +398, +428, +443, +444, +460, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/rescheduler.go':[ +71, +72, +83, +], +'go-project:sources/go/kubernetes/test/e2e/security_context.go':[ +189, +190, +], +'go-project:sources/go/kubernetes/test/e2e/service.go':[ +66, +289, +292, +319, +399, +426, +463, +593, +596, +624, +631, +660, +677, +717, +736, +763, +773, +956, +1013, +1105, +1128, +1148, +1372, +1407, +1472, +1473, +1570, +1575, +1583, +1600, +1630, +1632, +], +'go-project:sources/go/kubernetes/test/e2e/service_accounts.go':[ +84, +128, +163, +232, +235, +240, +252, +259, +359, +379, +], +'go-project:sources/go/kubernetes/test/e2e/serviceloadbalancers.go':[ +107, +174, +], +'go-project:sources/go/kubernetes/test/e2e/ssh.go':[ +86, +89, +92, +], +'go-project:sources/go/kubernetes/test/e2e/stackdriver_monitoring.go':[ +89, +103, +159, +], +'go-project:sources/go/kubernetes/test/e2e/statefulset.go':[ +675, +706, +747, +758, +805, +806, +819, +832, +1018, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pd.go':[ +76, +109, +124, +169, +184, +300, +339, +353, +397, +399, +450, +458, +468, +491, +511, +531, +676, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes-disruptive.go':[ +34, +139, +156, +173, +174, +212, +245, +291, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes-gce.go':[ +41, +149, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes-vsphere.go':[ +116, +122, +148, +164, +179, +194, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes.go':[ +36, +56, +158, +308, +313, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pv_reclaimpolicy.go':[ +69, +99, +112, +150, +170, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pvc_label_selector.go':[ +37, +87, +91, +99, +104, +134, +142, +145, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volume_provisioning.go':[ +65, +88, +465, +595, +626, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_utils.go':[ +92, +208, +278, +349, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_diskformat.go':[ +103, +126, +153, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_fstype.go':[ +89, +106, +119, +132, +136, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_ops_storm.go':[ +102, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_placement.go':[ +96, +166, +170, +181, +191, +209, +213, +228, +240, +329, +348, +368, +378, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_vsan_policy.go':[ +105, +106, +114, +115, +123, +124, +133, +134, +142, +143, +157, +158, +172, +173, +186, +187, +201, +211, +212, +221, +240, +241, +258, +260, +265, +289, +294, +299, +304, +], +'go-project:sources/go/kubernetes/test/e2e/third-party.go':[ +55, +132, +136, +158, +], +'go-project:sources/go/kubernetes/test/e2e/ubernetes_lite.go':[ +167, +186, +231, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/daemonsets.go':[ +138, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/deployments.go':[ +62, +71, +164, +173, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/ingress.go':[ +75, +117, +124, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/persistent_volumes.go':[ +105, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/sysctl.go':[ +93, +], +'go-project:sources/go/kubernetes/test/e2e_federation/apiserver.go':[ +41, +69, +72, +77, +103, +], +'go-project:sources/go/kubernetes/test/e2e_federation/deployment.go':[ +60, +143, +173, +175, +183, +188, +203, +213, +228, +242, +255, +257, +], +'go-project:sources/go/kubernetes/test/e2e_federation/event.go':[ +71, +], +'go-project:sources/go/kubernetes/test/e2e_federation/framework/cluster.go':[ +67, +104, +152, +216, +220, +], +'go-project:sources/go/kubernetes/test/e2e_federation/framework/crudtester.go':[ +42, +43, +], +'go-project:sources/go/kubernetes/test/e2e_federation/framework/framework.go':[ +106, +136, +139, +], +'go-project:sources/go/kubernetes/test/e2e_federation/framework/util.go':[ +99, +149, +153, +], +'go-project:sources/go/kubernetes/test/e2e_federation/ingress.go':[ +136, +283, +295, +313, +315, +325, +326, +332, +336, +339, +342, +365, +367, +368, +373, +376, +379, +393, +395, +412, +413, +415, +418, +421, +423, +453, +487, +508, +513, +544, +596, +610, +613, +], +'go-project:sources/go/kubernetes/test/e2e_federation/namespace.go':[ +191, +220, +222, +244, +255, +], +'go-project:sources/go/kubernetes/test/e2e_federation/replicaset.go':[ +114, +160, +165, +170, +186, +191, +197, +203, +223, +229, +234, +239, +257, +267, +269, +277, +298, +325, +353, +358, +386, +391, +407, +412, +416, +424, +430, +447, +457, +459, +470, +481, +522, +528, +], +'go-project:sources/go/kubernetes/test/e2e_federation/service.go':[ +104, +164, +167, +233, +321, +351, +353, +391, +], +'go-project:sources/go/kubernetes/test/e2e_federation/util.go':[ +44, +58, +83, +88, +89, +93, +98, +103, +106, +119, +149, +198, +200, +219, +230, +242, +250, +261, +386, +407, +], +'go-project:sources/go/kubernetes/test/e2e_node/benchmark_util.go':[ +131, +], +'go-project:sources/go/kubernetes/test/e2e_node/gpus.go':[ +133, +164, +], +'go-project:sources/go/kubernetes/test/e2e_node/node_problem_detector_linux.go':[ +197, +338, +342, +364, +434, +435, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/node_conformance.go':[ +93, +261, +295, +296, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/node_e2e.go':[ +104, +108, +122, +124, +135, +144, +146, +186, +214, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/remote.go':[ +32, +65, +66, +73, +107, +111, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/ssh.go':[ +44, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/types.go':[ +46, +], +'go-project:sources/go/kubernetes/test/e2e_node/resource_collector.go':[ +177, +181, +252, +456, +544, +546, +547, +548, +550, +], +'go-project:sources/go/kubernetes/test/e2e_node/runner/local/run_local.go':[ +35, +69, +], +'go-project:sources/go/kubernetes/test/e2e_node/runner/remote/run_remote.go':[ +20, +58, +61, +125, +402, +480, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/etcd.go':[ +41, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/kubelet.go':[ +63, +111, +150, +151, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/server.go':[ +37, +71, +93, +94, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/services.go':[ +144, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/package_validator.go':[ +156, +202, +], +'go-project:sources/go/kubernetes/test/e2e_node/util.go':[ +91, +94, +132, +183, +], +'go-project:sources/go/kubernetes/test/images/mount-tester/mt.go':[ +55, +], +'go-project:sources/go/kubernetes/test/images/netexec/netexec.go':[ +299, +313, +325, +338, +], +'go-project:sources/go/kubernetes/test/images/network-tester/webserver.go':[ +56, +95, +255, +], +'go-project:sources/go/kubernetes/test/images/no-snat-test/main.go':[ +149, +], +'go-project:sources/go/kubernetes/test/images/port-forward-tester/portforwardtester.go':[ +132, +], +'go-project:sources/go/kubernetes/test/images/porter/porter.go':[ +81, +], +'go-project:sources/go/kubernetes/test/images/resource-consumer/controller/controller.go':[ +33, +34, +94, +101, +127, +134, +161, +168, +192, +199, +206, +220, +231, +235, +], +'go-project:sources/go/kubernetes/test/images/resource-consumer/utils.go':[ +45, +], +'go-project:sources/go/kubernetes/test/integration/deployment/util.go':[ +91, +101, +133, +], +'go-project:sources/go/kubernetes/test/integration/federation/framework/controller.go':[ +33, +], +'go-project:sources/go/kubernetes/test/integration/federation/framework/crudtester.go':[ +44, +46, +], +'go-project:sources/go/kubernetes/test/integration/framework/master_utils.go':[ +129, +132, +181, +203, +221, +236, +238, +254, +345, +474, +504, +], +'go-project:sources/go/kubernetes/test/integration/framework/perf_utils.go':[ +40, +], +'go-project:sources/go/kubernetes/test/integration/scheduler_perf/util.go':[ +79, +], +'go-project:sources/go/kubernetes/test/soak/cauldron/cauldron.go':[ +99, +], +'go-project:sources/go/kubernetes/test/utils/deployment.go':[ +37, +51, +72, +73, +74, +116, +143, +144, +145, +203, +], +'go-project:sources/go/kubernetes/test/utils/runners.go':[ +244, +252, +321, +388, +451, +554, +592, +595, +601, +604, +629, +630, +718, +729, +1019, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/exec.go':[ +45, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S104.json b/its/ruling/src/test/resources/expected/go/go-S104.json new file mode 100644 index 00000000..7d745ac3 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S104.json @@ -0,0 +1,143 @@ +{ +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +0, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +0, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +0, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +0, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/api/types.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/api/v1/types.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/validation/validation.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_loadbalancer.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/generated/bindata.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/helpers.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +0, +], +'go-project:sources/go/kubernetes/pkg/volume/glusterfs/glusterfs.go':[ +0, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/types.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/types.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/cluster_size_autoscaling.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/common/projected.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/deployment.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/framework/ingress_utils.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/kubectl.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/predicates.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/service.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/statefulset.go':[ +0, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volume_provisioning.go':[ +0, +], +'go-project:sources/go/kubernetes/test/utils/runners.go':[ +0, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1067.json b/its/ruling/src/test/resources/expected/go/go-S1067.json new file mode 100644 index 00000000..8ed1c077 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1067.json @@ -0,0 +1,123 @@ +{ +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +672, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +707, +], +'go-project:sources/go/kubernetes/pkg/api/pod/util.go':[ +220, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +348, +], +'go-project:sources/go/kubernetes/pkg/api/validation/schema.go':[ +354, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +391, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_controllerCommon.go':[ +155, +210, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_storage.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go':[ +811, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go':[ +556, +565, +597, +606, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +856, +], +'go-project:sources/go/kubernetes/pkg/controller/disruption/disruption.go':[ +714, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/replica_calculator.go':[ +144, +257, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set_utils.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/controller/replication/replication_controller_utils.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/tokens_controller.go':[ +770, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_utils.go':[ +116, +300, +387, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +813, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/config.go':[ +200, +435, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/helpers.go':[ +389, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +1621, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +1916, +], +'go-project:sources/go/kubernetes/pkg/master/thirdparty/thirdparty.go':[ +238, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/policy_comparator.go':[ +147, +], +'go-project:sources/go/kubernetes/pkg/util/tolerations/tolerations.go':[ +106, +], +'go-project:sources/go/kubernetes/pkg/volume/testing/testing.go':[ +788, +], +'go-project:sources/go/kubernetes/pkg/volume/validation/pv_validation.go':[ +38, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +166, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +566, +567, +585, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/reflect/type.go':[ +61, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go':[ +104, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +787, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/helpers.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go':[ +291, +], +'go-project:sources/go/kubernetes/test/e2e/framework/firewall_util.go':[ +360, +], +'go-project:sources/go/kubernetes/test/e2e/perf/load.go':[ +171, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/reflect/type.go':[ +61, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S107.json b/its/ruling/src/test/resources/expected/go/go-S107.json new file mode 100644 index 00000000..44c50e18 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S107.json @@ -0,0 +1,211 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S107.go':[ +4, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/client_generator.go':[ +68, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +449, +557, +684, +858, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/join.go':[ +321, +344, +537, +646, +], +'go-project:sources/go/kubernetes/pkg/api/unversioned/time.go':[ +55, +], +'go-project:sources/go/kubernetes/pkg/apis/meta/v1/time.go':[ +59, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +65, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce.go':[ +245, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +855, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +199, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/horizontal.go':[ +101, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/attach_detach_controller.go':[ +81, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/default_storage_factory_builder.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply.go':[ +507, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/attach.go':[ +98, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/delete.go':[ +247, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/exec.go':[ +103, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +682, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/fake_runtime.go':[ +314, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/runtime_mock.go':[ +93, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_service.go':[ +149, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_streaming.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/exec.go':[ +66, +137, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/eviction_manager.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +1443, +1461, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +223, +557, +2156, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/httpstream.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/portforward.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/websocket.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/attach.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/exec.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/server.go':[ +121, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/streaming/server.go':[ +334, +338, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go':[ +78, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/reconciler/reconciler.go':[ +90, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/volume_manager.go':[ +147, +], +'go-project:sources/go/kubernetes/pkg/kubemark/hollow_kubelet.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/kubemark/hollow_proxy.go':[ +63, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +445, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +156, +164, +185, +986, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +361, +], +'go-project:sources/go/kubernetes/plugin/cmd/kube-scheduler/app/configurator.go':[ +76, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +106, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/micro_time.go':[ +59, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/time.go':[ +59, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go':[ +54, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/attributes.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +613, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +863, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/storage_factory.go':[ +30, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/storage_decorator.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_watcher.go':[ +108, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +122, +], +'go-project:sources/go/kubernetes/test/e2e/common/autoscaling_utils.go':[ +102, +108, +120, +420, +], +'go-project:sources/go/kubernetes/test/e2e/framework/networking_utils.go':[ +174, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +817, +], +'go-project:sources/go/kubernetes/test/e2e/perf/load.go':[ +404, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/node_conformance.go':[ +261, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/node_e2e.go':[ +186, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/remote.go':[ +66, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/server.go':[ +71, +], +'go-project:sources/go/kubernetes/test/utils/deployment.go':[ +145, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S108.json b/its/ruling/src/test/resources/expected/go/go-S108.json new file mode 100644 index 00000000..584b9a55 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S108.json @@ -0,0 +1,14 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S108.go':[ +10, +18, +22, +], +'go-project:ruling/src/test/resources/sources/go/S1862.go':[ +4, +6, +], +'go-project:sources/go/kubernetes/pkg/controller/service/servicecontroller.go':[ +179, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1110.json b/its/ruling/src/test/resources/expected/go/go-S1110.json new file mode 100644 index 00000000..3c733912 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1110.json @@ -0,0 +1,5 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1110.go':[ +4, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1125.json b/its/ruling/src/test/resources/expected/go/go-S1125.json new file mode 100644 index 00000000..ab9bf38c --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1125.json @@ -0,0 +1,5 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1125.go':[ +4, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1134.json b/its/ruling/src/test/resources/expected/go/go-S1134.json new file mode 100644 index 00000000..087b2537 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1134.json @@ -0,0 +1,27 @@ +{ +'go-project:sources/go/kubernetes/cmd/hyperkube/kube-proxy.go':[ +43, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +541, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go':[ +341, +], +'go-project:sources/go/kubernetes/pkg/util/mount/mount.go':[ +164, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/attacher.go':[ +200, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/attacher.go':[ +263, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/plugins.go':[ +61, +191, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/watch/encoder.go':[ +51, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1135.json b/its/ruling/src/test/resources/expected/go/go-S1135.json new file mode 100644 index 00000000..f8415a86 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1135.json @@ -0,0 +1,4176 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1135.go':[ +6, +7, +], +'go-project:sources/go/kubernetes/cmd/gendocs/gen_kubectl_docs.go':[ +49, +], +'go-project:sources/go/kubernetes/cmd/genman/gen_kube_man.go':[ +99, +132, +], +'go-project:sources/go/kubernetes/cmd/genyaml/gen_kubectl_yaml.go':[ +68, +89, +], +'go-project:sources/go/kubernetes/cmd/gke-certificates-controller/main.go':[ +34, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/options/options.go':[ +64, +165, +211, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/options/validation.go':[ +23, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +218, +265, +394, +467, +516, +625, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/batch.go':[ +47, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/certificates.go':[ +67, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/controllermanager.go':[ +338, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/core.go':[ +117, +234, +284, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/options/options.go':[ +145, +148, +151, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/conntrack.go':[ +82, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/server.go':[ +110, +385, +403, +490, +514, +529, +545, +565, +616, +683, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/proxy.go':[ +35, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/env.go':[ +28, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1/register.go':[ +32, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation/validation.go':[ +40, +129, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/cmd.go':[ +66, +68, +71, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/defaults.go':[ +61, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/init.go':[ +89, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/phases/certs.go':[ +48, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/phases/validate.go':[ +39, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/token.go':[ +112, +211, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/discovery/discovery.go':[ +36, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/master/manifests.go':[ +194, +220, +434, +515, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/master/selfhosted.go':[ +88, +171, +180, +252, +304, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/apiconfig/clusterroles.go':[ +49, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/apiconfig/setupmaster.go':[ +37, +98, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/certs.go':[ +34, +98, +128, +220, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/pkiutil/pki_helpers.go':[ +30, +31, +159, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/kubeconfig/kubeconfig.go':[ +44, +47, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/token/bootstrap.go':[ +140, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/validate/validate.go':[ +84, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/preflight/checks.go':[ +143, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/bootstrap.go':[ +47, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/options/container_runtime.go':[ +53, +127, +134, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/options/options.go':[ +63, +149, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +191, +304, +363, +611, +822, +827, +876, +877, +898, +980, +1027, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/fake_client_generator.go':[ +34, +96, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/generator_fake_for_clientset.go':[ +84, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_clientset.go':[ +68, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/test_apis/testgroup/v1/register.go':[ +28, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/types/helpers.go':[ +32, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +79, +171, +198, +204, +267, +372, +490, +700, +725, +938, +947, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +148, +207, +436, +464, +726, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/namer.go':[ +145, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/package.go':[ +163, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +112, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/generators/openapi.go':[ +425, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/mungedocs.go':[ +53, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/toc.go':[ +35, +], +'go-project:sources/go/kubernetes/examples/https-nginx/make_secret.go':[ +37, +], +'go-project:sources/go/kubernetes/examples/sharing-clusters/make_secret.go':[ +31, +], +'go-project:sources/go/kubernetes/federation/apis/core/register.go':[ +55, +], +'go-project:sources/go/kubernetes/federation/apis/core/v1/register.go':[ +33, +], +'go-project:sources/go/kubernetes/federation/apis/federation/v1beta1/register.go':[ +32, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/core.go':[ +26, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/options/validation.go':[ +30, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/server.go':[ +230, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +183, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/route53.go':[ +55, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/stubs/route53api.go':[ +56, +85, +90, +94, +98, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/rrchangeset.go':[ +86, +111, +121, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/interfaces/interfaces.go':[ +36, +37, +38, +39, +43, +45, +49, +51, +52, +56, +58, +59, +60, +61, +62, +63, +64, +68, +69, +70, +71, +72, +76, +80, +81, +85, +86, +89, +91, +92, +93, +94, +98, +100, +104, +106, +110, +112, +113, +117, +120, +121, +122, +123, +124, +128, +129, +131, +132, +133, +137, +146, +147, +148, +149, +150, +151, +155, +159, +163, +167, +172, +176, +179, +180, +181, +183, +188, +189, +191, +192, +199, +204, +205, +211, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs/clouddns.go':[ +28, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/rrchangeset.go':[ +67, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/rrstype/rrstype.go':[ +27, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/registry.go':[ +39, +53, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/cluster/cluster_client.go':[ +149, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/deployment/deploymentcontroller.go':[ +357, +523, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +53, +528, +891, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/replicaset/replicasetcontroller.go':[ +337, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/dns/dns.go':[ +293, +366, +393, +442, +449, +510, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +669, +706, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/sync/controller.go':[ +228, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/delaying_deliverer.go':[ +17, +26, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/deletionhelper/deletion_helper.go':[ +137, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/eventsink/eventsink.go':[ +47, +53, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_informer.go':[ +407, +487, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_updater.go':[ +99, +112, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/planner/planner.go':[ +151, +152, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/podanalyzer/pod_helper.go':[ +37, +41, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +17, +357, +885, +888, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/join.go':[ +51, +143, +612, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/util/util.go':[ +263, +], +'go-project:sources/go/kubernetes/federation/registry/cluster/etcd/etcd.go':[ +65, +], +'go-project:sources/go/kubernetes/hack/cmd/teststale/teststale.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/api/annotation_key_constants.go':[ +77, +103, +], +'go-project:sources/go/kubernetes/pkg/api/errors/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/api/helper/helpers.go':[ +51, +263, +290, +439, +524, +578, +592, +], +'go-project:sources/go/kubernetes/pkg/api/meta/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/api/objectreference.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/api/ref/ref.go':[ +39, +55, +], +'go-project:sources/go/kubernetes/pkg/api/register.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/api/taint.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/api/testapi/testapi.go':[ +19, +387, +426, +443, +], +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +55, +168, +539, +], +'go-project:sources/go/kubernetes/pkg/api/toleration.go':[ +17, +24, +], +'go-project:sources/go/kubernetes/pkg/api/types.go':[ +229, +603, +651, +685, +719, +766, +795, +917, +1675, +1685, +1929, +1963, +2030, +2098, +2339, +2818, +3325, +3432, +3439, +3469, +3482, +3487, +3783, +], +'go-project:sources/go/kubernetes/pkg/api/util/group_version.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/api/v1/annotation_key_constants.go':[ +77, +103, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +34, +397, +438, +463, +560, +598, +718, +], +'go-project:sources/go/kubernetes/pkg/api/v1/defaults.go':[ +32, +54, +], +'go-project:sources/go/kubernetes/pkg/api/v1/generate.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/api/v1/helper/helpers.go':[ +80, +107, +450, +504, +518, +], +'go-project:sources/go/kubernetes/pkg/api/v1/node/util.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +52, +78, +94, +], +'go-project:sources/go/kubernetes/pkg/api/v1/ref/ref.go':[ +39, +55, +], +'go-project:sources/go/kubernetes/pkg/api/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/api/v1/toleration.go':[ +21, +46, +], +'go-project:sources/go/kubernetes/pkg/api/v1/types.go':[ +31, +258, +737, +862, +946, +1073, +1106, +1858, +1869, +1982, +2149, +2183, +2250, +2320, +3233, +3802, +3837, +3950, +3960, +3994, +4006, +4011, +4337, +], +'go-project:sources/go/kubernetes/pkg/api/validation/schema.go':[ +369, +373, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +56, +299, +345, +814, +1496, +1997, +2112, +2143, +2151, +2477, +2498, +2549, +2645, +2745, +2774, +2778, +3240, +3325, +3335, +3379, +3395, +3685, +4043, +], +'go-project:sources/go/kubernetes/pkg/apimachinery/tests/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/apis/abac/latest/latest.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/apis/abac/register.go':[ +31, +38, +], +'go-project:sources/go/kubernetes/pkg/apis/abac/types.go':[ +64, +66, +69, +], +'go-project:sources/go/kubernetes/pkg/apis/abac/v0/register.go':[ +31, +43, +], +'go-project:sources/go/kubernetes/pkg/apis/abac/v1beta1/register.go':[ +31, +43, +], +'go-project:sources/go/kubernetes/pkg/apis/admission/v1alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/v1alpha1/register.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/validation/validation.go':[ +55, +202, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/register.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/types.go':[ +105, +127, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/conversion.go':[ +43, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/types.go':[ +158, +180, +353, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/validation/validation.go':[ +36, +55, +170, +], +'go-project:sources/go/kubernetes/pkg/apis/authentication/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/authentication/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/authorization/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/authorization/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v2alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/v2alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/validation/validation.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/apis/certificates/v1beta1/register.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/helpers.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/register.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/types.go':[ +89, +175, +461, +472, +714, +719, +724, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/register.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/types.go':[ +85, +522, +534, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/register.go':[ +47, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/types.go':[ +142, +257, +387, +620, +638, +680, +698, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/defaults.go':[ +33, +108, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/types.go':[ +253, +389, +632, +650, +692, +710, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/validation/validation.go':[ +375, +403, +449, +471, +510, +545, +], +'go-project:sources/go/kubernetes/pkg/apis/imagepolicy/v1alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/policy/register.go':[ +47, +], +'go-project:sources/go/kubernetes/pkg/apis/policy/v1alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/policy/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1alpha1/register.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1beta1/register.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/validation/validation.go':[ +106, +150, +212, +221, +], +'go-project:sources/go/kubernetes/pkg/apis/settings/v1alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/settings/validation/validation.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/apis/storage/util/helpers.go':[ +23, +29, +43, +], +'go-project:sources/go/kubernetes/pkg/apis/storage/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/storage/v1/util/helpers.go':[ +23, +29, +43, +], +'go-project:sources/go/kubernetes/pkg/apis/storage/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/storage/v1beta1/util/helpers.go':[ +23, +29, +43, +], +'go-project:sources/go/kubernetes/pkg/auth/authorizer/abac/abac.go':[ +53, +175, +231, +], +'go-project:sources/go/kubernetes/pkg/auth/user/doc.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/capabilities/capabilities.go':[ +49, +], +'go-project:sources/go/kubernetes/pkg/client/chaosclient/chaosclient.go':[ +134, +135, +136, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/event_expansion.go':[ +149, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/extensions/v1beta1/scale_expansion.go':[ +35, +52, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/event_expansion.go':[ +150, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/extensions/internalversion/scale_expansion.go':[ +35, +52, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/generic.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/generic.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/client/leaderelection/resourcelock/configmaplock.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/client/leaderelection/resourcelock/interface.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/internalversion/service_expansion.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/client/listers/core/v1/service_expansion.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/internalversion/deployment_expansion.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/client/listers/extensions/v1beta1/deployment_expansion.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/client/listers/policy/internalversion/poddisruptionbudget_expansion.go':[ +58, +], +'go-project:sources/go/kubernetes/pkg/client/listers/policy/v1beta1/poddisruptionbudget_expansion.go':[ +58, +], +'go-project:sources/go/kubernetes/pkg/client/retry/util.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/helper.go':[ +34, +39, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/cloud.go':[ +64, +92, +122, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +188, +210, +321, +354, +401, +894, +963, +972, +981, +1017, +1124, +1125, +1227, +1539, +1540, +1581, +1643, +1735, +2113, +2456, +2884, +3149, +3331, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +84, +125, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go':[ +31, +186, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/instances.go':[ +79, +144, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/retry_handler.go':[ +149, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/volumes.go':[ +66, +78, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_loadbalancer.go':[ +64, +109, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_storage.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce.go':[ +488, +502, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_backendservice.go':[ +79, +140, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_cert.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_clusters.go':[ +36, +49, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_forwardingrule.go':[ +89, +104, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_healthchecks.go':[ +97, +149, +199, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_instancegroup.go':[ +66, +75, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_instances.go':[ +370, +506, +537, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go':[ +103, +626, +663, +892, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_routes.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_targetproxy.go':[ +85, +155, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_urlmap.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/mesos/client.go':[ +216, +304, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/mesos/mesos.go':[ +207, +246, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go':[ +432, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +593, +607, +1222, +1238, +1269, +1335, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt.go':[ +216, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +95, +457, +], +'go-project:sources/go/kubernetes/pkg/controller/bootstrap/bootstrapsigner.go':[ +95, +], +'go-project:sources/go/kubernetes/pkg/controller/bootstrap/tokencleaner.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +311, +], +'go-project:sources/go/kubernetes/pkg/controller/controller_ref_manager.go':[ +255, +], +'go-project:sources/go/kubernetes/pkg/controller/controller_utils.go':[ +187, +188, +189, +260, +295, +298, +404, +472, +703, +742, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/cronjob_controller.go':[ +73, +218, +225, +268, +283, +301, +309, +365, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/injection.go':[ +76, +206, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/utils.go':[ +202, +241, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +129, +267, +278, +562, +625, +1057, +1062, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/update.go':[ +350, +365, +402, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/util/daemonset_util.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/deployment_controller.go':[ +104, +581, +661, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/progress.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/sync.go':[ +180, +198, +221, +366, +483, +516, +598, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/deployment_util.go':[ +69, +132, +296, +549, +558, +582, +947, +957, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/pod_util.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/replicaset_util.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/controller/disruption/disruption.go':[ +166, +323, +539, +], +'go-project:sources/go/kubernetes/pkg/controller/endpoint/endpoints_controller.go':[ +90, +257, +312, +358, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/garbagecollector.go':[ +167, +203, +218, +294, +316, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/graph_builder.go':[ +137, +178, +317, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/metaonly/types.go':[ +25, +34, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/operations.go':[ +97, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/rate_limiter_helper.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/controller/job/jobcontroller.go':[ +85, +318, +435, +], +'go-project:sources/go/kubernetes/pkg/controller/namespace/deletion/namespaced_resources_deleter.go':[ +166, +264, +356, +397, +508, +], +'go-project:sources/go/kubernetes/pkg/controller/node/controller_utils.go':[ +177, +197, +286, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +141, +867, +892, +], +'go-project:sources/go/kubernetes/pkg/controller/node/range_allocator.go':[ +38, +227, +], +'go-project:sources/go/kubernetes/pkg/controller/node/rate_limited_queue.go':[ +201, +266, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/horizontal.go':[ +53, +113, +182, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set.go':[ +146, +314, +447, +563, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set_utils.go':[ +51, +], +'go-project:sources/go/kubernetes/pkg/controller/replication/replication_controller.go':[ +141, +309, +442, +577, +], +'go-project:sources/go/kubernetes/pkg/controller/replication/replication_controller_utils.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/controller/resourcequota/resource_quota_controller.go':[ +382, +], +'go-project:sources/go/kubernetes/pkg/controller/route/routecontroller.go':[ +46, +89, +], +'go-project:sources/go/kubernetes/pkg/controller/service/servicecontroller.go':[ +289, +301, +338, +500, +533, +551, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/serviceaccounts_controller.go':[ +210, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_pod_control.go':[ +201, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set.go':[ +138, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_utils.go':[ +175, +], +'go-project:sources/go/kubernetes/pkg/controller/ttl/ttlcontroller.go':[ +22, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/attach_detach_controller.go':[ +91, +430, +446, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/statusupdater/node_status_updater.go':[ +64, +118, +], +'go-project:sources/go/kubernetes/pkg/conversion/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/conversion/queryparams/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/aws/aws_credentials.go':[ +146, +176, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/gcp/metadata.go':[ +168, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/plugins.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/features/kube_features.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/fields/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/admission/initializer.go':[ +31, +109, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/authenticator/config.go':[ +68, +142, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/options/cloudprovider.go':[ +57, +], +'go-project:sources/go/kubernetes/pkg/kubectl/apply.go':[ +95, +105, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply.go':[ +406, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/attach.go':[ +216, +270, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo.go':[ +73, +114, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go':[ +136, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/cmd.go':[ +201, +274, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/view.go':[ +75, +80, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/convert.go':[ +156, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/cp.go':[ +140, +176, +191, +233, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/delete.go':[ +338, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/describe.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/drain.go':[ +296, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/exec.go':[ +88, +311, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/explain.go':[ +80, +88, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/get.go':[ +147, +423, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/label.go':[ +210, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/patch.go':[ +152, +251, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/portforward.go':[ +92, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollingupdate.go':[ +320, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/run.go':[ +217, +235, +387, +465, +578, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/helper.go':[ +75, +138, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/taint.go':[ +159, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/top_pod.go':[ +142, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/clientcache.go':[ +121, +125, +203, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +728, +729, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editor.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory.go':[ +71, +72, +87, +101, +104, +106, +108, +143, +235, +473, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_builder.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_client_access.go':[ +220, +245, +260, +267, +283, +305, +370, +371, +458, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_object_mapping.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +447, +472, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/printing.go':[ +111, +161, +], +'go-project:sources/go/kubernetes/pkg/kubectl/generate.go':[ +32, +], +'go-project:sources/go/kubernetes/pkg/kubectl/history.go':[ +71, +153, +162, +221, +222, +305, +], +'go-project:sources/go/kubernetes/pkg/kubectl/kubectl.go':[ +148, +], +'go-project:sources/go/kubernetes/pkg/kubectl/metricsutil/metrics_client.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/kubectl/pdb.go':[ +174, +], +'go-project:sources/go/kubernetes/pkg/kubectl/proxy_server.go':[ +47, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/builder.go':[ +90, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/helper.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/result.go':[ +269, +277, +280, +285, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/visitor.go':[ +367, +496, +506, +537, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rollback.go':[ +288, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +167, +710, +], +'go-project:sources/go/kubernetes/pkg/kubectl/run.go':[ +103, +816, +], +'go-project:sources/go/kubernetes/pkg/kubectl/scale.go':[ +471, +], +'go-project:sources/go/kubernetes/pkg/kubectl/secret_for_tls.go':[ +108, +120, +], +'go-project:sources/go/kubernetes/pkg/kubectl/service_basic.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/kubectl/sorting_printer.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/kubectl/stop.go':[ +222, +263, +347, +368, +405, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/cadvisor_linux.go':[ +50, +149, +223, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go':[ +102, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager.go':[ +21, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_linux.go':[ +180, +207, +390, +652, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/node_container_manager.go':[ +142, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/config.go':[ +138, +140, +338, +352, +454, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/container_gc.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/container_reference_manager.go':[ +41, +56, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/helpers.go':[ +65, +108, +139, +221, +257, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/ref.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/runtime.go':[ +71, +87, +92, +103, +109, +111, +188, +381, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/runtime_cache.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/sync_result.go':[ +26, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/fake_runtime.go':[ +252, +], +'go-project:sources/go/kubernetes/pkg/kubelet/custommetrics/custom_metrics.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/cm/container_manager_linux.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_checkpoint.go':[ +110, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_container.go':[ +93, +123, +339, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_image.go':[ +50, +94, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_legacy.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go':[ +159, +169, +201, +246, +327, +368, +543, +557, +631, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_service.go':[ +65, +78, +111, +274, +331, +363, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_streaming.go':[ +124, +146, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/exec.go':[ +65, +74, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/helpers.go':[ +339, +340, +381, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/helpers_linux.go':[ +51, +52, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/client.go':[ +98, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/fake_client.go':[ +391, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/helpers.go':[ +134, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/kube_docker_client.go':[ +50, +138, +318, +431, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/legacy.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/naming.go':[ +40, +97, +104, +125, +132, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/remote/docker_service.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/securitycontext/provider.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/kubelet/envvars/envvars.go':[ +65, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/eviction_manager.go':[ +192, +223, +250, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/helpers.go':[ +96, +385, +638, +], +'go-project:sources/go/kubernetes/pkg/kubelet/gpu/nvidia/nvidia_gpu_manager.go':[ +37, +63, +69, +188, +237, +], +'go-project:sources/go/kubernetes/pkg/kubelet/images/image_gc_manager.go':[ +172, +], +'go-project:sources/go/kubernetes/pkg/kubelet/images/types.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +140, +205, +214, +414, +416, +513, +549, +647, +679, +1036, +1067, +1305, +1360, +1479, +1493, +1721, +1766, +1767, +1774, +1795, +1902, +1997, +2055, +2135, +2146, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_network.go':[ +37, +82, +209, +270, +296, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_node_status.go':[ +279, +281, +288, +411, +433, +435, +436, +528, +532, +556, +564, +619, +700, +742, +800, +922, +948, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +277, +327, +618, +635, +678, +851, +865, +884, +1029, +1057, +1059, +1076, +1362, +1412, +1437, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_resources.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_volumes.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/helpers.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_container.go':[ +131, +404, +495, +496, +765, +788, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc.go':[ +349, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_image.go':[ +136, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_logs.go':[ +45, +291, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go':[ +160, +175, +832, +908, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_sandbox.go':[ +63, +90, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/labels.go':[ +32, +], +'go-project:sources/go/kubernetes/pkg/kubelet/leaky/leaky.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/kubelet/lifecycle/handlers.go':[ +58, +], +'go-project:sources/go/kubernetes/pkg/kubelet/metrics/metrics.go':[ +85, +209, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/cni/cni.go':[ +264, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport.go':[ +132, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport_manager.go':[ +186, +234, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_linux.go':[ +180, +311, +338, +354, +355, +371, +397, +426, +444, +449, +549, +716, +795, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/network.go':[ +19, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/plugins.go':[ +72, +76, +80, +123, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/testing/mock_network_plugin.go':[ +19, +], +'go-project:sources/go/kubernetes/pkg/kubelet/networks.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pleg/generic.go':[ +122, +236, +302, +341, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod/mirror_client.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod/pod_manager.go':[ +165, +170, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod_workers.go':[ +185, +], +'go-project:sources/go/kubernetes/pkg/kubelet/prober/prober_manager.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/kubelet/prober/worker.go':[ +197, +], +'go-project:sources/go/kubernetes/pkg/kubelet/qos/policy.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/kubelet/reason_cache.go':[ +36, +37, +], +'go-project:sources/go/kubernetes/pkg/kubelet/remote/remote_runtime.go':[ +83, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/cap.go':[ +19, +61, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/image.go':[ +43, +50, +149, +190, +221, +242, +250, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/log.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +98, +118, +128, +132, +157, +202, +248, +254, +354, +452, +605, +685, +697, +748, +834, +996, +1027, +1048, +1061, +1204, +1205, +1207, +1475, +1587, +1703, +1782, +1798, +1806, +1808, +2155, +2215, +2220, +2318, +2378, +2380, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/systemd.go':[ +49, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/app-interface.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/fake-app-interface.go':[ +106, +117, +141, +185, +214, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/imagestore.go':[ +25, +35, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/pod-level-interface.go':[ +28, +], +'go-project:sources/go/kubernetes/pkg/kubelet/runonce.go':[ +93, +139, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/httpstream.go':[ +56, +244, +295, +343, +397, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/websocket.go':[ +105, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/server.go':[ +456, +457, +462, +467, +526, +546, +548, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/stats/fs_resource_analyzer.go':[ +101, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/stats/summary.go':[ +76, +363, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/streaming/server.go':[ +104, +196, +213, +], +'go-project:sources/go/kubernetes/pkg/kubelet/status/status_manager.go':[ +358, +422, +445, +500, +541, +], +'go-project:sources/go/kubernetes/pkg/kubelet/sysctl/runtime.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/kubelet/types/types.go':[ +26, +], +'go-project:sources/go/kubernetes/pkg/labels/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/master/client_ca_hook.go':[ +55, +], +'go-project:sources/go/kubernetes/pkg/master/controller.go':[ +170, +172, +], +'go-project:sources/go/kubernetes/pkg/master/master.go':[ +114, +177, +253, +261, +397, +], +'go-project:sources/go/kubernetes/pkg/master/ports/ports.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/master/services.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/master/tunneler/ssh.go':[ +189, +], +'go-project:sources/go/kubernetes/pkg/printers/humanreadable.go':[ +49, +277, +342, +368, +635, +650, +688, +759, +], +'go-project:sources/go/kubernetes/pkg/printers/interface.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +1880, +2350, +2642, +3102, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +98, +113, +448, +], +'go-project:sources/go/kubernetes/pkg/printers/json.go':[ +61, +102, +], +'go-project:sources/go/kubernetes/pkg/printers/jsonpath.go':[ +33, +154, +], +'go-project:sources/go/kubernetes/pkg/printers/name.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/printers/template.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/printers/versioned.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +160, +205, +721, +788, +974, +1393, +1504, +1644, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +383, +413, +637, +686, +687, +854, +917, +1023, +1026, +1060, +1067, +1072, +1075, +1084, +1091, +1110, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxysocket.go':[ +100, +178, +211, +228, +234, +252, +274, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/roundrobin.go':[ +128, +], +'go-project:sources/go/kubernetes/pkg/proxy/util/conntrack.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxier.go':[ +39, +462, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +41, +138, +216, +335, +521, +547, +553, +571, +593, +], +'go-project:sources/go/kubernetes/pkg/quota/evaluator/core/persistent_volume_claims.go':[ +65, +], +'go-project:sources/go/kubernetes/pkg/quota/evaluator/core/pods.go':[ +56, +117, +141, +256, +], +'go-project:sources/go/kubernetes/pkg/quota/generic/evaluator.go':[ +134, +], +'go-project:sources/go/kubernetes/pkg/quota/install/registry.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/externaladmissionhookconfiguration/storage/storage.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/storage/storage.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/rest/storage_apiserver.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/rest/storage_apps.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/statefulset/storage/storage.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/statefulset/strategy.go':[ +149, +], +'go-project:sources/go/kubernetes/pkg/registry/authentication/rest/storage_authentication.go':[ +37, +44, +], +'go-project:sources/go/kubernetes/pkg/registry/authorization/rest/storage_authorization.go':[ +45, +], +'go-project:sources/go/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/storage/storage.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/registry/autoscaling/rest/storage_autoscaling.go':[ +36, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/cronjob/storage/storage.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/job/storage/storage.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/job/strategy.go':[ +74, +126, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/rest/storage_batch.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/registry/cachesize/cachesize.go':[ +65, +79, +85, +114, +], +'go-project:sources/go/kubernetes/pkg/registry/certificates/certificates/storage/storage.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/registry/certificates/rest/storage_certificates.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/registry/core/componentstatus/validator.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/registry/core/configmap/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/core/endpoint/storage/storage.go':[ +49, +], +'go-project:sources/go/kubernetes/pkg/registry/core/endpoint/strategy.go':[ +103, +], +'go-project:sources/go/kubernetes/pkg/registry/core/event/storage/storage.go':[ +38, +43, +62, +], +'go-project:sources/go/kubernetes/pkg/registry/core/limitrange/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/core/limitrange/strategy.go':[ +85, +], +'go-project:sources/go/kubernetes/pkg/registry/core/namespace/storage/storage.go':[ +71, +143, +], +'go-project:sources/go/kubernetes/pkg/registry/core/node/storage/storage.go':[ +110, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolume/storage/storage.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolumeclaim/storage/storage.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/rest/log.go':[ +45, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/storage/eviction.go':[ +112, +116, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/storage/storage.go':[ +85, +144, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/strategy.go':[ +167, +197, +235, +], +'go-project:sources/go/kubernetes/pkg/registry/core/podtemplate/storage/storage.go':[ +51, +], +'go-project:sources/go/kubernetes/pkg/registry/core/replicationcontroller/storage/storage.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/registry/core/resourcequota/storage/storage.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/registry/core/rest/storage_core.go':[ +85, +161, +171, +271, +], +'go-project:sources/go/kubernetes/pkg/registry/core/secret/storage/storage.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/allocator/bitmap.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/allocator/storage/storage.go':[ +43, +44, +45, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/ipallocator/controller/repair.go':[ +49, +50, +91, +154, +158, +162, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/portallocator/controller/repair.go':[ +77, +137, +141, +145, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +83, +91, +105, +115, +138, +147, +161, +210, +358, +386, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/storage/storage.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/strategy.go':[ +90, +], +'go-project:sources/go/kubernetes/pkg/registry/core/serviceaccount/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/daemonset/storage/storage.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/daemonset/strategy.go':[ +88, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/deployment/storage/storage.go':[ +79, +258, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/ingress/storage/storage.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/networkpolicy/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/podsecuritypolicy/storage/storage.go':[ +51, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/replicaset/storage/storage.go':[ +76, +187, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/rest/storage_extensions.go':[ +52, +68, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/rest/thirdparty_controller.go':[ +56, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresource/storage/storage.go':[ +39, +42, +59, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/codec.go':[ +143, +162, +300, +479, +566, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/storage/storage.go':[ +94, +98, +115, +], +'go-project:sources/go/kubernetes/pkg/registry/networking/networkpolicy/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/networking/rest/storage_settings.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/registry/policy/poddisruptionbudget/storage/storage.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/registry/policy/poddisruptionbudget/strategy.go':[ +142, +], +'go-project:sources/go/kubernetes/pkg/registry/policy/rest/storage_policy.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrole/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrolebinding/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rest/storage_rbac.go':[ +70, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/role/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rolebinding/registry.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rolebinding/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/policy_comparator.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/rule.go':[ +280, +], +'go-project:sources/go/kubernetes/pkg/registry/registrytest/endpoint.go':[ +41, +49, +70, +97, +], +'go-project:sources/go/kubernetes/pkg/registry/registrytest/etcd.go':[ +148, +161, +164, +], +'go-project:sources/go/kubernetes/pkg/registry/settings/podpreset/storage/storage.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/registry/settings/rest/storage_settings.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/registry/storage/rest/storage_storage.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/registry/storage/storageclass/storage/storage.go':[ +51, +], +'go-project:sources/go/kubernetes/pkg/runtime/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/runtime/serializer/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/runtime/serializer/json/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/runtime/serializer/protobuf/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/runtime/serializer/recognizer/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/runtime/serializer/streaming/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/runtime/serializer/versioning/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/security/apparmor/helpers.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/provider.go':[ +228, +], +'go-project:sources/go/kubernetes/pkg/securitycontext/util.go':[ +163, +], +'go-project:sources/go/kubernetes/pkg/serviceaccount/util.go':[ +55, +], +'go-project:sources/go/kubernetes/pkg/ssh/ssh.go':[ +68, +363, +416, +], +'go-project:sources/go/kubernetes/pkg/types/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/bandwidth/linux.go':[ +247, +], +'go-project:sources/go/kubernetes/pkg/util/errors/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/framer/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/i18n/i18n.go':[ +93, +], +'go-project:sources/go/kubernetes/pkg/util/intstr/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/io/writer.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/util/ipconfig/ipconfig.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/iptables.go':[ +162, +253, +452, +], +'go-project:sources/go/kubernetes/pkg/util/json/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/labels/labels.go':[ +79, +], +'go-project:sources/go/kubernetes/pkg/util/logs/logs.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/util/mount/mount.go':[ +17, +], +'go-project:sources/go/kubernetes/pkg/util/mount/nsenter_mount.go':[ +51, +82, +191, +], +'go-project:sources/go/kubernetes/pkg/util/net/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/netsh/netsh.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/util/netsh/testing/fake.go':[ +55, +], +'go-project:sources/go/kubernetes/pkg/util/oom/oom.go':[ +21, +], +'go-project:sources/go/kubernetes/pkg/util/rand/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/runtime/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/sets/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/sets/types/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/system/system_utils.go':[ +23, +], +'go-project:sources/go/kubernetes/pkg/util/util.go':[ +43, +], +'go-project:sources/go/kubernetes/pkg/util/validation/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/validation/field/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/wait/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/util/yaml/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/version/base.go':[ +37, +49, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/aws_ebs.go':[ +303, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/aws_util.go':[ +127, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_common.go':[ +163, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_file/azure_file.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_file/azure_provision.go':[ +162, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder.go':[ +312, +330, +363, +418, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder_util.go':[ +144, +186, +], +'go-project:sources/go/kubernetes/pkg/volume/downwardapi/downwardapi.go':[ +135, +170, +237, +], +'go-project:sources/go/kubernetes/pkg/volume/empty_dir/empty_dir.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/volume/fc/disk_manager.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/volume/fc/fc_util.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/volume/flocker/flocker.go':[ +311, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/gce_pd.go':[ +266, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/gce_util.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/volume/git_repo/git_repo.go':[ +162, +], +'go-project:sources/go/kubernetes/pkg/volume/iscsi/disk_manager.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/volume/local/local.go':[ +127, +154, +252, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/attacher.go':[ +73, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/photon_pd.go':[ +206, +290, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/photon_util.go':[ +51, +59, +], +'go-project:sources/go/kubernetes/pkg/volume/plugins.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/disk_manager.go':[ +20, +50, +], +'go-project:sources/go/kubernetes/pkg/volume/testing/testing.go':[ +217, +], +'go-project:sources/go/kubernetes/pkg/volume/util/atomic_writer.go':[ +248, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go':[ +157, +667, +724, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go':[ +592, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume.go':[ +212, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume_util.go':[ +138, +], +'go-project:sources/go/kubernetes/pkg/watch/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/pkg/watch/json/types.go':[ +30, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/antiaffinity/admission.go':[ +65, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/imagepolicy/admission.go':[ +224, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialization/initialization.go':[ +127, +135, +171, +221, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/admission.go':[ +51, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/data_source.go':[ +30, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/influxdb.go':[ +36, +44, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/limitranger/admission.go':[ +112, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/persistentvolume/label/admission.go':[ +127, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podnodeselector/admission.go':[ +45, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/apis/podtolerationrestriction/v1alpha1/register.go':[ +31, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/register.go':[ +46, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1/register.go':[ +31, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/controller.go':[ +56, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/resource_access.go':[ +123, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/security/podsecuritypolicy/admission.go':[ +280, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/serviceaccount/admission.go':[ +94, +175, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/webhook/admission.go':[ +195, +244, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authenticator/token/bootstrap/bootstrap.go':[ +37, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/node/graph_populator.go':[ +94, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go':[ +99, +140, +144, +146, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go':[ +103, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go':[ +104, +107, +127, +158, +325, +413, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +183, +669, +676, +944, +1007, +1039, +1052, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/balanced_resource_allocation.go':[ +39, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity.go':[ +166, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go':[ +61, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/selector_spreading.go':[ +38, +231, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util/non_zero.go':[ +33, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/types.go':[ +30, +34, +35, +40, +41, +54, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go':[ +42, +133, +142, +151, +221, +227, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/api/register.go':[ +26, +30, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/api/v1/register.go':[ +26, +37, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/equivalence_cache.go':[ +105, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/generic_scheduler.go':[ +266, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +60, +204, +232, +427, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/scheduler.go':[ +92, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/schedulercache/reconcile_affinity.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1/defaults.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/apiserver.go':[ +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_discovery.go':[ +29, +75, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/generic.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/generic.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server/start.go':[ +89, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/finalizer/crd_finalizer.go':[ +215, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go':[ +55, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/etcd.go':[ +52, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/start.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/equality/semantic.go':[ +32, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go':[ +426, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/default.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/help.go':[ +30, +70, +138, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/interfaces.go':[ +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/meta.go':[ +41, +78, +94, +125, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go':[ +17, +70, +105, +136, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/amount.go':[ +83, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go':[ +78, +230, +375, +444, +646, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/scale_int.go':[ +82, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/codec.go':[ +58, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/fuzzer.go':[ +79, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go':[ +193, +209, +238, +280, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go':[ +37, +45, +55, +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go':[ +33, +266, +287, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apimachinery/announced/group_factory.go':[ +108, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apimachinery/registered/registered.go':[ +35, +258, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apimachinery/types.go':[ +36, +37, +47, +57, +68, +79, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/labels.go':[ +30, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go':[ +24, +76, +85, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/register.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go':[ +486, +775, +780, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go':[ +45, +83, +278, +580, +618, +742, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/watch.go':[ +44, +62, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1alpha1/types.go':[ +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +472, +662, +790, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/unstructured/converter.go':[ +311, +386, +519, +577, +623, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +827, +856, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/codec.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/codec_check.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/embedded.go':[ +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/extension.go':[ +36, +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/helper.go':[ +115, +124, +132, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/schema/group_version.go':[ +173, +174, +194, +221, +222, +271, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go':[ +57, +146, +442, +501, +572, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/codec_factory.go':[ +96, +97, +163, +182, +183, +198, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/negotiated_codec.go':[ +23, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go':[ +109, +114, +236, +339, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf_extension.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go':[ +29, +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/types.go':[ +86, +95, +105, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/clock/clock.go':[ +232, +238, +244, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go':[ +127, +133, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go':[ +50, +201, +300, +316, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/intstr/intstr.go':[ +38, +60, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/interface.go':[ +36, +80, +111, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/port_range.go':[ +52, +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/runtime/runtime.go':[ +43, +84, +119, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +180, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go':[ +74, +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go':[ +192, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/yaml/decoder.go':[ +104, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/version/types.go':[ +20, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/watch/mux.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/watch/until.go':[ +72, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/json/fields.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go':[ +181, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1/register.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/fuzzer/fuzzer.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/v1/register.go':[ +42, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/request.go':[ +52, +133, +148, +152, +181, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/scheme.go':[ +17, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go':[ +199, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate.go':[ +49, +283, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go':[ +165, +232, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/response.go':[ +37, +51, +76, +108, +109, +132, +139, +146, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go':[ +49, +54, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +84, +90, +319, +325, +354, +407, +470, +529, +532, +552, +853, +948, +1064, +1094, +1100, +1227, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go':[ +64, +86, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +156, +432, +434, +484, +486, +493, +504, +536, +539, +746, +765, +786, +908, +934, +951, +987, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go':[ +35, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/context.go':[ +33, +70, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/doc.go':[ +19, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestcontext.go':[ +72, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go':[ +55, +74, +179, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/storage_factory.go':[ +50, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +67, +364, +536, +906, +952, +973, +1116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/storage_decorator.go':[ +58, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/meta.go':[ +42, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/rest.go':[ +33, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/update.go':[ +63, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/config.go':[ +115, +138, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/cors.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.go':[ +33, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/timeout.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/genericapiserver.go':[ +61, +70, +224, +230, +236, +448, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/handler.go':[ +142, +152, +164, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/httplog/log.go':[ +52, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go':[ +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/etcd.go':[ +86, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/server_run_options.go':[ +111, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/resource_encoding_config.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/storage_codec.go':[ +82, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/storage_factory.go':[ +148, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +223, +318, +335, +538, +645, +920, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/errors/storage.go':[ +32, +47, +62, +77, +94, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_helper.go':[ +64, +102, +526, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_watcher.go':[ +282, +363, +389, +447, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/metrics/metrics.go':[ +107, +108, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/testing/utils.go':[ +51, +238, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/compact.go':[ +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go':[ +542, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/interfaces.go':[ +68, +75, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/names/generate.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/selection_predicate.go':[ +78, +88, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/etcd2.go':[ +69, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/storagebackend/factory/factory.go':[ +35, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/watch_cache.go':[ +224, +427, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/logs/logs.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/transport.go':[ +68, +216, +236, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/webhook/webhook.go':[ +64, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/audit/webhook/webhook.go':[ +61, +141, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go':[ +264, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/discovery_client.go':[ +200, +263, +336, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/restmapper.go':[ +103, +111, +114, +157, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/dynamic/client.go':[ +253, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go':[ +124, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/generic.go':[ +66, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/event_expansion.go':[ +149, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/extensions/v1beta1/scale_expansion.go':[ +35, +52, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/core/v1/service_expansion.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/extensions/v1beta1/deployment_expansion.go':[ +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/listers/policy/v1beta1/poddisruptionbudget_expansion.go':[ +58, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/annotation_key_constants.go':[ +77, +103, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/helper/helpers.go':[ +51, +263, +290, +439, +524, +578, +592, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/objectreference.go':[ +17, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/register.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/taint.go':[ +17, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/toleration.go':[ +17, +24, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/types.go':[ +229, +603, +651, +685, +719, +766, +795, +917, +1675, +1685, +1929, +1963, +2030, +2098, +2339, +2818, +3325, +3432, +3439, +3469, +3482, +3487, +3783, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/annotation_key_constants.go':[ +77, +103, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +34, +397, +438, +463, +560, +598, +718, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/defaults.go':[ +32, +54, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/generate.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/node/util.go':[ +17, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/ref/ref.go':[ +39, +55, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/toleration.go':[ +21, +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/types.go':[ +31, +258, +737, +862, +946, +1073, +1106, +1858, +1869, +1982, +2149, +2183, +2250, +2320, +3233, +3802, +3837, +3950, +3960, +3994, +4006, +4011, +4337, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/admissionregistration/v1alpha1/register.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/register.go':[ +48, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/types.go':[ +105, +127, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/types.go':[ +158, +180, +353, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authentication/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authentication/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authorization/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/authorization/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v2alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/batch/v2alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/certificates/v1beta1/register.go':[ +42, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/register.go':[ +47, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/types.go':[ +142, +257, +387, +620, +638, +680, +698, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/defaults.go':[ +33, +108, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/types.go':[ +253, +389, +632, +650, +692, +710, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/policy/register.go':[ +47, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/policy/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1alpha1/register.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1beta1/register.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/settings/v1alpha1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/storage/v1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/storage/v1beta1/register.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/util/util.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/version/base.go':[ +37, +49, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/client.go':[ +76, +140, +157, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/config.go':[ +71, +328, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/fake/fake.go':[ +93, +106, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +453, +470, +614, +786, +800, +837, +1016, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/url_utils.go':[ +76, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/versions.go':[ +33, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/watch/encoder.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/fake.go':[ +218, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/fixture.go':[ +95, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/funcs.go':[ +263, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/auth/clientauth.go':[ +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/controller.go':[ +60, +137, +141, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go':[ +43, +46, +47, +216, +280, +285, +506, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/listwatch.go':[ +98, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/mutation_cache.go':[ +38, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/reflector.go':[ +170, +384, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/shared_informer.go':[ +447, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/store.go':[ +74, +93, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/register.go':[ +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/types.go':[ +30, +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/register.go':[ +25, +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/types.go':[ +29, +33, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go':[ +544, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/loader.go':[ +422, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/portforward/portforward.go':[ +35, +235, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/event.go':[ +113, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/remotecommand.go':[ +74, +118, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/v1.go':[ +126, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/v2.go':[ +113, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/workqueue/delaying_queue.go':[ +71, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/v1beta1/register.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go':[ +162, +356, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiservice_controller.go':[ +208, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/handler_proxy.go':[ +178, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/externalversions/generic.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/internalversion/generic.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/cmd/server/start.go':[ +113, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go':[ +185, +299, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/etcd/etcd.go':[ +50, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/apis/metrics/doc.go':[ +17, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1/register.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/apiserver/apiserver.go':[ +47, +50, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go':[ +88, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/etcd.go':[ +48, +], +'go-project:sources/go/kubernetes/test/e2e/addon_update.go':[ +37, +355, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/cluster_size_autoscaling.go':[ +321, +363, +412, +1128, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/horizontal_pod_autoscaling.go':[ +115, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/es.go':[ +33, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/sd_utils.go':[ +115, +], +'go-project:sources/go/kubernetes/test/e2e/common/autoscaling_utils.go':[ +107, +], +'go-project:sources/go/kubernetes/test/e2e/common/container_probe.go':[ +239, +], +'go-project:sources/go/kubernetes/test/e2e/common/downwardapi_volume.go':[ +417, +], +'go-project:sources/go/kubernetes/test/e2e/common/host_path.go':[ +32, +38, +160, +217, +], +'go-project:sources/go/kubernetes/test/e2e/common/init_container.go':[ +306, +], +'go-project:sources/go/kubernetes/test/e2e/common/kubelet_etc_hosts.go':[ +91, +], +'go-project:sources/go/kubernetes/test/e2e/common/pods.go':[ +380, +], +'go-project:sources/go/kubernetes/test/e2e/common/util.go':[ +36, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_restart.go':[ +263, +], +'go-project:sources/go/kubernetes/test/e2e/deployment.go':[ +126, +532, +687, +728, +781, +817, +838, +855, +913, +1437, +], +'go-project:sources/go/kubernetes/test/e2e/dns.go':[ +65, +251, +319, +368, +], +'go-project:sources/go/kubernetes/test/e2e/e2e.go':[ +180, +352, +], +'go-project:sources/go/kubernetes/test/e2e/empty.go':[ +37, +], +'go-project:sources/go/kubernetes/test/e2e/examples.go':[ +168, +249, +296, +380, +], +'go-project:sources/go/kubernetes/test/e2e/extension/initializers.go':[ +40, +], +'go-project:sources/go/kubernetes/test/e2e/framework/framework.go':[ +55, +617, +], +'go-project:sources/go/kubernetes/test/e2e/framework/get-kubemark-resource-usage.go':[ +39, +], +'go-project:sources/go/kubernetes/test/e2e/framework/google_compute.go':[ +34, +], +'go-project:sources/go/kubernetes/test/e2e/framework/ingress_utils.go':[ +83, +319, +568, +692, +870, +952, +], +'go-project:sources/go/kubernetes/test/e2e/framework/kubelet_stats.go':[ +46, +], +'go-project:sources/go/kubernetes/test/e2e/framework/log_size_monitoring.go':[ +88, +], +'go-project:sources/go/kubernetes/test/e2e/framework/metrics_util.go':[ +299, +], +'go-project:sources/go/kubernetes/test/e2e/framework/networking_utils.go':[ +212, +233, +240, +268, +], +'go-project:sources/go/kubernetes/test/e2e/framework/nodes_util.go':[ +69, +156, +246, +267, +], +'go-project:sources/go/kubernetes/test/e2e/framework/perf_util.go':[ +25, +], +'go-project:sources/go/kubernetes/test/e2e/framework/pods.go':[ +187, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +52, +69, +358, +486, +972, +1239, +1262, +1310, +], +'go-project:sources/go/kubernetes/test/e2e/framework/size.go':[ +42, +43, +61, +62, +82, +83, +], +'go-project:sources/go/kubernetes/test/e2e/framework/statefulset_utils.go':[ +209, +716, +725, +], +'go-project:sources/go/kubernetes/test/e2e/framework/test_context.go':[ +41, +213, +246, +247, +261, +273, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +109, +135, +182, +235, +244, +954, +1116, +1131, +1147, +1193, +1723, +1944, +2452, +2921, +3610, +3627, +4004, +4076, +4089, +4203, +4234, +4270, +4625, +5239, +5424, +5440, +], +'go-project:sources/go/kubernetes/test/e2e/framework/volume_util.go':[ +184, +], +'go-project:sources/go/kubernetes/test/e2e/garbage_collector.go':[ +566, +674, +], +'go-project:sources/go/kubernetes/test/e2e/ingress.go':[ +64, +131, +142, +156, +], +'go-project:sources/go/kubernetes/test/e2e/kibana_logging.go':[ +35, +], +'go-project:sources/go/kubernetes/test/e2e/kubectl.go':[ +109, +115, +121, +127, +133, +262, +1430, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet.go':[ +100, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet_perf.go':[ +71, +107, +237, +], +'go-project:sources/go/kubernetes/test/e2e/logging_soak.go':[ +43, +], +'go-project:sources/go/kubernetes/test/e2e/networking.go':[ +65, +82, +91, +193, +], +'go-project:sources/go/kubernetes/test/e2e/networking_perf.go':[ +39, +45, +46, +], +'go-project:sources/go/kubernetes/test/e2e/nodeoutofdisk.go':[ +41, +], +'go-project:sources/go/kubernetes/test/e2e/nvidia-gpus.go':[ +129, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +133, +339, +410, +], +'go-project:sources/go/kubernetes/test/e2e/perf/load.go':[ +71, +91, +], +'go-project:sources/go/kubernetes/test/e2e/perftype/perftype.go':[ +19, +], +'go-project:sources/go/kubernetes/test/e2e/portforward.go':[ +49, +], +'go-project:sources/go/kubernetes/test/e2e/proxy.go':[ +214, +306, +317, +], +'go-project:sources/go/kubernetes/test/e2e/rc.go':[ +108, +], +'go-project:sources/go/kubernetes/test/e2e/reboot.go':[ +83, +], +'go-project:sources/go/kubernetes/test/e2e/replica_set.go':[ +120, +], +'go-project:sources/go/kubernetes/test/e2e/resize_nodes.go':[ +130, +176, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/opaque_resource.go':[ +60, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/predicates.go':[ +961, +], +'go-project:sources/go/kubernetes/test/e2e/security_context.go':[ +111, +120, +128, +136, +], +'go-project:sources/go/kubernetes/test/e2e/service.go':[ +66, +74, +134, +279, +334, +390, +436, +840, +894, +962, +1603, +], +'go-project:sources/go/kubernetes/test/e2e/serviceloadbalancers.go':[ +96, +188, +], +'go-project:sources/go/kubernetes/test/e2e/stackdriver_monitoring.go':[ +111, +], +'go-project:sources/go/kubernetes/test/e2e/statefulset.go':[ +989, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes-local.go':[ +53, +83, +196, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes.go':[ +55, +74, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volume_provisioning.go':[ +793, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volumes.go':[ +144, +], +'go-project:sources/go/kubernetes/test/e2e/ubernetes_lite.go':[ +54, +381, +382, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/deployments.go':[ +31, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/persistent_volumes.go':[ +63, +], +'go-project:sources/go/kubernetes/test/e2e_federation/authn.go':[ +32, +80, +], +'go-project:sources/go/kubernetes/test/e2e_federation/framework/util.go':[ +88, +149, +], +'go-project:sources/go/kubernetes/test/e2e_federation/ingress.go':[ +134, +158, +259, +267, +412, +532, +], +'go-project:sources/go/kubernetes/test/e2e_federation/service.go':[ +81, +252, +296, +], +'go-project:sources/go/kubernetes/test/e2e_federation/upgrade.go':[ +137, +], +'go-project:sources/go/kubernetes/test/e2e_federation/util.go':[ +44, +349, +], +'go-project:sources/go/kubernetes/test/e2e_node/builder/build.go':[ +89, +], +'go-project:sources/go/kubernetes/test/e2e_node/container.go':[ +31, +], +'go-project:sources/go/kubernetes/test/e2e_node/doc.go':[ +18, +], +'go-project:sources/go/kubernetes/test/e2e_node/environment/conformance.go':[ +51, +], +'go-project:sources/go/kubernetes/test/e2e_node/image_list.go':[ +80, +93, +], +'go-project:sources/go/kubernetes/test/e2e_node/node_problem_detector_linux.go':[ +69, +229, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/node_conformance.go':[ +152, +174, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/node_e2e.go':[ +41, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/remote.go':[ +65, +167, +190, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/utils.go':[ +82, +], +'go-project:sources/go/kubernetes/test/e2e_node/runner/remote/run_remote.go':[ +99, +152, +490, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/apiserver.go':[ +45, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/etcd.go':[ +33, +34, +37, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/internal_services.go':[ +73, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/kubelet.go':[ +35, +49, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/server.go':[ +100, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/docker_validator.go':[ +45, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/types.go':[ +24, +74, +90, +131, +132, +163, +], +'go-project:sources/go/kubernetes/test/e2e_node/util.go':[ +47, +], +'go-project:sources/go/kubernetes/test/integration/framework/master_utils.go':[ +85, +90, +91, +120, +126, +128, +134, +265, +294, +432, +437, +444, +], +'go-project:sources/go/kubernetes/test/integration/framework/perf_utils.go':[ +60, +], +'go-project:sources/go/kubernetes/test/utils/conditions.go':[ +114, +], +'go-project:sources/go/kubernetes/test/utils/runners.go':[ +1113, +1172, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/wal.go':[ +227, +228, +478, +508, +525, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/wal.go':[ +225, +226, +474, +506, +526, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/funcs.go':[ +263, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1145.json b/its/ruling/src/test/resources/expected/go/go-S1145.json new file mode 100644 index 00000000..95d91c62 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1145.json @@ -0,0 +1,12 @@ +{ +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +56, +169, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/fuzzer.go':[ +80, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/fuzzer/fuzzer.go':[ +35, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1151.json b/its/ruling/src/test/resources/expected/go/go-S1151.json new file mode 100644 index 00000000..d8a8041f --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1151.json @@ -0,0 +1,869 @@ +{ +'go-project:sources/go/kubernetes/cmd/kubelet/app/auth.go':[ +89, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +1005, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +352, +354, +792, +841, +857, +870, +887, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +114, +307, +310, +320, +336, +449, +476, +507, +521, +569, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/package.go':[ +151, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +116, +155, +187, +200, +215, +230, +258, +307, +334, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/generators/openapi.go':[ +340, +468, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/rrchangeset.go':[ +83, +110, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +776, +810, +818, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/dns/dns.go':[ +205, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +553, +], +'go-project:sources/go/kubernetes/pkg/api/pod/util.go':[ +65, +135, +], +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +700, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +185, +732, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +36, +153, +223, +], +'go-project:sources/go/kubernetes/pkg/api/validation/schema.go':[ +383, +391, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +2146, +2814, +2832, +3640, +3651, +3662, +3678, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/validation/validation.go':[ +81, +90, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/helpers.go':[ +217, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1alpha1/helpers.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1beta1/helpers.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/validation/validation.go':[ +198, +211, +220, +], +'go-project:sources/go/kubernetes/pkg/client/conditions/conditions.go':[ +45, +82, +101, +121, +], +'go-project:sources/go/kubernetes/pkg/client/leaderelection/resourcelock/interface.go':[ +81, +90, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/conditions.go':[ +162, +199, +218, +238, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +1712, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go':[ +599, +], +'go-project:sources/go/kubernetes/pkg/controller/client_builder.go':[ +151, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +741, +1128, +1141, +1152, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/update.go':[ +129, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/progress.go':[ +104, +127, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/sync.go':[ +331, +360, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/deployment_util.go':[ +901, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/garbagecollector.go':[ +336, +347, +375, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/graph_builder.go':[ +475, +495, +515, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +808, +816, +], +'go-project:sources/go/kubernetes/pkg/controller/node/testutil/test_utils.go':[ +302, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/horizontal.go':[ +249, +265, +280, +450, +], +'go-project:sources/go/kubernetes/pkg/controller/resourcequota/replenishment_controller.go':[ +114, +127, +139, +150, +161, +172, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/tokens_controller.go':[ +247, +288, +298, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_pod_control.go':[ +189, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/populator/desired_state_of_world_populator.go':[ +129, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller.go':[ +972, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/authorizer/config.go':[ +108, +125, +134, +145, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply.go':[ +576, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go':[ +226, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_view_last_applied.go':[ +139, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/auth/cani.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go':[ +46, +64, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/set.go':[ +139, +174, +199, +210, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_clusterrolebinding.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_configmap.go':[ +92, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_pdb.go':[ +84, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_rolebinding.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_secret.go':[ +104, +185, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_service.go':[ +94, +150, +206, +267, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/logs.go':[ +231, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/run.go':[ +360, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/testdata/edit/record.go':[ +92, +99, +108, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +173, +335, +552, +568, +689, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_client_access.go':[ +517, +588, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +142, +153, +155, +171, +211, +224, +266, +758, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/version.go':[ +78, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/builder.go':[ +135, +402, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +527, +], +'go-project:sources/go/kubernetes/pkg/kubectl/sorting_printer.go':[ +86, +187, +201, +211, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go':[ +129, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/config.go':[ +164, +188, +270, +280, +292, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/file.go':[ +93, +142, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/file_linux.go':[ +118, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +578, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +450, +485, +548, +557, +584, +1010, +1105, +1136, +1169, +1294, +1503, +1523, +1557, +], +'go-project:sources/go/kubernetes/pkg/kubelet/lifecycle/handlers.go':[ +56, +65, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport.go':[ +112, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pleg/generic.go':[ +154, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util/csr/csr.go':[ +95, +], +'go-project:sources/go/kubernetes/pkg/printers/customcolumn.go':[ +206, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +229, +237, +1255, +1309, +1354, +2611, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +295, +715, +1724, +], +'go-project:sources/go/kubernetes/pkg/printers/json.go':[ +41, +85, +], +'go-project:sources/go/kubernetes/pkg/printers/jsonpath.go':[ +47, +61, +], +'go-project:sources/go/kubernetes/pkg/printers/printers.go':[ +55, +66, +81, +92, +113, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +1717, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxysocket.go':[ +61, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +94, +], +'go-project:sources/go/kubernetes/pkg/quota/evaluator/core/pods.go':[ +205, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/strategy.go':[ +295, +365, +381, +394, +454, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/ipallocator/controller/repair.go':[ +144, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/portallocator/controller/repair.go':[ +127, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +311, +324, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/rest/thirdparty_controller.go':[ +81, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/codec.go':[ +348, +478, +517, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_role.go':[ +128, +139, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go':[ +124, +140, +151, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/rule.go':[ +210, +], +'go-project:sources/go/kubernetes/pkg/serviceaccount/jwt.go':[ +147, +221, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/iptables.go':[ +605, +], +'go-project:sources/go/kubernetes/pkg/volume/glusterfs/glusterfs.go':[ +937, +949, +979, +989, +], +'go-project:sources/go/kubernetes/pkg/volume/nfs/nfs.go':[ +197, +], +'go-project:sources/go/kubernetes/pkg/volume/util.go':[ +104, +109, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume_util.go':[ +118, +204, +212, +220, +228, +236, +244, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialization/initialization.go':[ +141, +188, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/noderestriction/admission.go':[ +100, +122, +161, +187, +206, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +550, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +267, +317, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/schedulercache/cache.go':[ +170, +225, +262, +289, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go':[ +178, +186, +194, +426, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go':[ +293, +439, +461, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/help.go':[ +105, +133, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/meta.go':[ +99, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go':[ +224, +237, +269, +279, +330, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/amount.go':[ +116, +132, +176, +187, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/math.go':[ +67, +158, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go':[ +300, +429, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go':[ +121, +159, +202, +219, +240, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/suffix.go':[ +143, +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go':[ +35, +53, +68, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go':[ +334, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go':[ +113, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go':[ +609, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/cloner.go':[ +175, +190, +203, +214, +227, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +598, +622, +635, +648, +671, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/unstructured/converter.go':[ +148, +171, +179, +438, +445, +452, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +131, +189, +456, +567, +678, +702, +713, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/helper.go':[ +159, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go':[ +130, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go':[ +138, +176, +186, +197, +214, +357, +409, +422, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/recognizer/recognizer.go':[ +68, +93, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/swagger_doc_generator.go':[ +75, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go':[ +134, +152, +169, +197, +225, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/intstr/intstr.go':[ +168, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/json/json.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/jsonmergepatch/patch.go':[ +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go':[ +94, +96, +111, +113, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +273, +310, +465, +555, +581, +734, +1425, +1794, +2082, +2090, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go':[ +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/watch/streamwatcher.go':[ +104, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/reflect/deep_equal.go':[ +149, +158, +184, +191, +292, +301, +317, +335, +342, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/response.go':[ +49, +74, +107, +133, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go':[ +36, +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +659, +672, +698, +741, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +280, +388, +439, +587, +618, +650, +666, +683, +706, +728, +747, +766, +787, +796, +898, +913, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/decorated_watcher.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +368, +383, +837, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi.go':[ +390, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +729, +862, +869, +876, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_watcher.go':[ +275, +286, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go':[ +275, +283, +291, +325, +332, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/dial.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/proxy.go':[ +107, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/transport.go':[ +176, +207, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/wsstream/conn.go':[ +278, +329, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go':[ +240, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +185, +732, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/helpers.go':[ +216, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1alpha1/helpers.go':[ +76, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1beta1/helpers.go':[ +76, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +535, +548, +757, +959, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/actions.go':[ +228, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/fixture.go':[ +87, +106, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/funcs.go':[ +120, +134, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/controller.go':[ +301, +368, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/shared_informer.go':[ +343, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/event.go':[ +196, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/v4.go':[ +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/jsonpath.go':[ +216, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/sd_utils.go':[ +209, +], +'go-project:sources/go/kubernetes/test/e2e/cluster_upgrade.go':[ +216, +226, +], +'go-project:sources/go/kubernetes/test/e2e/common/autoscaling_utils.go':[ +318, +325, +332, +463, +], +'go-project:sources/go/kubernetes/test/e2e/common/init_container.go':[ +262, +297, +397, +], +'go-project:sources/go/kubernetes/test/e2e/deployment.go':[ +1234, +1244, +1261, +1279, +], +'go-project:sources/go/kubernetes/test/e2e/e2e.go':[ +71, +110, +], +'go-project:sources/go/kubernetes/test/e2e/framework/metrics_util.go':[ +319, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +1339, +1346, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +1443, +5420, +5432, +], +'go-project:sources/go/kubernetes/test/e2e/service_accounts.go':[ +95, +], +'go-project:sources/go/kubernetes/test/e2e/stackdriver_monitoring.go':[ +124, +], +'go-project:sources/go/kubernetes/test/e2e_node/image_list.go':[ +103, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/package_validator.go':[ +302, +], +'go-project:sources/go/kubernetes/test/list/main.go':[ +187, +204, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/repair.go':[ +44, +60, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/wal.go':[ +254, +263, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/repair.go':[ +44, +60, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/wal.go':[ +252, +261, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/expansion/expand.go':[ +86, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/reflect/deep_equal.go':[ +149, +158, +184, +191, +292, +301, +317, +335, +342, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/funcs.go':[ +120, +134, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S117.json b/its/ruling/src/test/resources/expected/go/go-S117.json new file mode 100644 index 00000000..2b76cb2d --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S117.json @@ -0,0 +1,151 @@ +{ +'go-project:sources/go/kubernetes/cmd/gke-certificates-controller/app/gke_signer.go':[ +102, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/completion.go':[ +138, +293, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/clouddns.go':[ +107, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/rrsets_list_call.go':[ +50, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/rrsets_service.go':[ +40, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs/rrsets_list_call.go':[ +49, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/internal/stubs/rrsets_service.go':[ +80, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_instances.go':[ +554, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go':[ +628, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_instances.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +941, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_routes.go':[ +254, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_volumes.go':[ +87, +107, +], +'go-project:sources/go/kubernetes/pkg/controller/node/controller_utils.go':[ +278, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/completion.go':[ +143, +299, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/testing/mock_network_plugin.go':[ +66, +66, +74, +74, +74, +85, +85, +105, +105, +105, +125, +125, +125, +], +'go-project:sources/go/kubernetes/pkg/util/io/writer.go':[ +56, +61, +71, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/iptables.go':[ +607, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_common.go':[ +220, +242, +], +'go-project:sources/go/kubernetes/pkg/volume/cephfs/cephfs.go':[ +133, +271, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder_util.go':[ +237, +], +'go-project:sources/go/kubernetes/pkg/volume/configmap/configmap.go':[ +269, +], +'go-project:sources/go/kubernetes/pkg/volume/fc/fc_util.go':[ +57, +73, +74, +94, +], +'go-project:sources/go/kubernetes/pkg/volume/iscsi/iscsi_util.go':[ +381, +413, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/photon_util.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd_util.go':[ +52, +111, +126, +], +'go-project:sources/go/kubernetes/pkg/volume/secret/secret.go':[ +268, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/negotiated_codec.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi_aggregator.go':[ +413, +414, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/dns_autoscaling.go':[ +49, +50, +51, +], +'go-project:sources/go/kubernetes/test/e2e/firewall.go':[ +35, +], +'go-project:sources/go/kubernetes/test/e2e/framework/nodes_util.go':[ +34, +34, +56, +56, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +1813, +3886, +], +'go-project:sources/go/kubernetes/test/e2e/kubectl.go':[ +1054, +1056, +], +'go-project:sources/go/kubernetes/test/e2e/no-snat.go':[ +95, +], +'go-project:sources/go/kubernetes/test/e2e/stackdriver_monitoring.go':[ +154, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pvc_label_selector.go':[ +134, +134, +134, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_ops_storm.go':[ +52, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1186.json b/its/ruling/src/test/resources/expected/go/go-S1186.json new file mode 100644 index 00000000..db0bcfb6 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1186.json @@ -0,0 +1,352 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S107.go':[ +7, +10, +], +'go-project:ruling/src/test/resources/sources/go/S108.go':[ +25, +], +'go-project:sources/go/kubernetes/cluster/images/etcd/rollback/rollback.go':[ +272, +], +'go-project:sources/go/kubernetes/cmd/cloud-controller-manager/app/controllermanager.go':[ +69, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +102, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/controllermanager.go':[ +88, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +126, +], +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/import_known_versions.go':[ +24, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/server.go':[ +69, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +71, +], +'go-project:sources/go/kubernetes/federation/registry/cluster/strategy.go':[ +80, +], +'go-project:sources/go/kubernetes/pkg/client/metrics/metrics.go':[ +57, +61, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +913, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure.go':[ +355, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/cloudstack/cloudstack.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/fake/fake.go':[ +87, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/mesos/mesos.go':[ +94, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go':[ +269, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/ovirt/ovirt.go':[ +121, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/photon/photon.go':[ +297, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace.go':[ +222, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +240, +], +'go-project:sources/go/kubernetes/pkg/controller/node/testutil/test_utils.go':[ +361, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/rate_limitters.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/templates/markdown.go':[ +123, +124, +125, +126, +127, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/testing/fake.go':[ +211, +448, +455, +458, +], +'go-project:sources/go/kubernetes/pkg/kubelet/configmap/configmap_manager.go':[ +70, +73, +], +'go-project:sources/go/kubernetes/pkg/kubelet/configmap/fake_manager.go':[ +36, +39, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/fake_cache.go':[ +42, +45, +48, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/securitycontext/fake.go':[ +32, +34, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/fake_iptables.go':[ +327, +330, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/plugins.go':[ +229, +], +'go-project:sources/go/kubernetes/pkg/kubelet/prober/testing/fake_manager.go':[ +27, +28, +29, +30, +], +'go-project:sources/go/kubernetes/pkg/kubelet/secret/fake_manager.go':[ +36, +39, +], +'go-project:sources/go/kubernetes/pkg/kubelet/secret/secret_manager.go':[ +70, +73, +], +'go-project:sources/go/kubernetes/pkg/kubemark/hollow_proxy.go':[ +50, +54, +55, +56, +57, +58, +59, +60, +61, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +513, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/roundrobin.go':[ +357, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxier.go':[ +448, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/roundrobin.go':[ +346, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/externaladmissionhookconfiguration/strategy.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/registry/admissionregistration/initializerconfiguration/strategy.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/controllerrevision/strategy.go':[ +56, +], +'go-project:sources/go/kubernetes/pkg/registry/apps/statefulset/strategy.go':[ +89, +], +'go-project:sources/go/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/cronjob/strategy.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/job/strategy.go':[ +132, +], +'go-project:sources/go/kubernetes/pkg/registry/certificates/certificates/strategy.go':[ +100, +156, +], +'go-project:sources/go/kubernetes/pkg/registry/core/configmap/strategy.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/registry/core/endpoint/strategy.go':[ +51, +55, +], +'go-project:sources/go/kubernetes/pkg/registry/core/event/strategy.go':[ +47, +50, +59, +], +'go-project:sources/go/kubernetes/pkg/registry/core/limitrange/strategy.go':[ +54, +63, +], +'go-project:sources/go/kubernetes/pkg/registry/core/namespace/strategy.go':[ +89, +], +'go-project:sources/go/kubernetes/pkg/registry/core/node/strategy.go':[ +82, +133, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolume/strategy.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/registry/core/persistentvolumeclaim/strategy.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/strategy.go':[ +88, +], +'go-project:sources/go/kubernetes/pkg/registry/core/podtemplate/strategy.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/registry/core/replicationcontroller/strategy.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/registry/core/resourcequota/strategy.go':[ +69, +], +'go-project:sources/go/kubernetes/pkg/registry/core/secret/strategy.go':[ +54, +61, +68, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/strategy.go':[ +69, +], +'go-project:sources/go/kubernetes/pkg/registry/core/serviceaccount/strategy.go':[ +57, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/daemonset/strategy.go':[ +107, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/deployment/strategy.go':[ +72, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/ingress/strategy.go':[ +83, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/networkpolicy/strategy.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/podsecuritypolicy/strategy.go':[ +62, +65, +68, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/replicaset/strategy.go':[ +95, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresource/strategy.go':[ +55, +63, +70, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/strategy.go':[ +53, +61, +68, +], +'go-project:sources/go/kubernetes/pkg/registry/networking/networkpolicy/strategy.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/registry/policy/poddisruptionbudget/strategy.go':[ +81, +], +'go-project:sources/go/kubernetes/pkg/registry/settings/podpreset/strategy.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/registry/storage/storageclass/strategy.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/util/async/bounded_frequency_runner.go':[ +57, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/testing/fake.go':[ +96, +98, +], +'go-project:sources/go/kubernetes/plugin/cmd/kube-scheduler/app/server.go':[ +59, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1/defaults.go':[ +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go':[ +59, +62, +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/strategy.go':[ +48, +51, +66, +103, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured/unstructured.go':[ +60, +61, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/schema/interfaces.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/watch/watch.go':[ +76, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_watcher.go':[ +173, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/mutation_detector.go':[ +52, +54, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/shared_informer.go':[ +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/metrics/metrics.go':[ +57, +61, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/fake.go':[ +45, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/flowcontrol/throttle.go':[ +91, +112, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/workqueue/default_rate_limiters.go':[ +62, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/workqueue/metrics.go':[ +53, +54, +55, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/registry/apiservice/strategy.go':[ +76, +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/registry/wardle/strategy.go':[ +47, +50, +66, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +1335, +1336, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/priorities.go':[ +69, +], +'go-project:sources/go/kubernetes/test/images/net/main.go':[ +161, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1192.json b/its/ruling/src/test/resources/expected/go/go-S1192.json new file mode 100644 index 00000000..5eb2313f --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1192.json @@ -0,0 +1,1260 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1192.go':[ +15, +], +'go-project:sources/go/kubernetes/cmd/cloud-controller-manager/app/controllermanager.go':[ +66, +], +'go-project:sources/go/kubernetes/cmd/gke-certificates-controller/app/gke_certificates_controller.go':[ +45, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/hyperkube.go':[ +149, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/autoscaling.go':[ +45, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/options/options.go':[ +158, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/server.go':[ +506, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/images/images.go':[ +42, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/master/manifests.go':[ +96, +210, +279, +324, +324, +324, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/master/selfhosted.go':[ +91, +199, +204, +205, +213, +227, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/certs.go':[ +64, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/options/options.go':[ +207, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/generator_fake_for_type.go':[ +119, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_clientset.go':[ +76, +79, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_group.go':[ +95, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_type.go':[ +88, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/scheme/generator_for_scheme.go':[ +91, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +270, +686, +688, +691, +790, +837, +863, +866, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +493, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/types.go':[ +23, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/generators/openapi.go':[ +379, +575, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/tests/commontests.go':[ +32, +32, +174, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +355, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_informer.go':[ +339, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/test/test_helper.go':[ +254, +255, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +62, +63, +111, +633, +], +'go-project:sources/go/kubernetes/pkg/api/testing/compat/compatibility_tester.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +168, +169, +200, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +413, +893, +1889, +1964, +3668, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/validation/validation.go':[ +47, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/defaults.go':[ +58, +129, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +993, +1127, +1256, +2279, +2686, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_clusterid.go':[ +72, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +603, +686, +707, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_volumes.go':[ +178, +293, +340, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +517, +626, +628, +], +'go-project:sources/go/kubernetes/pkg/controller/certificates/approver/sarapprove.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +128, +307, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/cronjob_controller.go':[ +178, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/deployment_controller.go':[ +184, +380, +], +'go-project:sources/go/kubernetes/pkg/controller/disruption/disruption.go':[ +340, +], +'go-project:sources/go/kubernetes/pkg/controller/endpoint/endpoints_controller.go':[ +195, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/graph_builder.go':[ +241, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/horizontal.go':[ +253, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/metrics/rest_metrics_client.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/controller/resourcequota/resource_quota_controller.go':[ +159, +], +'go-project:sources/go/kubernetes/pkg/controller/service/servicecontroller.go':[ +432, +], +'go-project:sources/go/kubernetes/pkg/controller/ttl/ttlcontroller.go':[ +134, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller.go':[ +598, +702, +715, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller_base.go':[ +205, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/keyring.go':[ +92, +], +'go-project:sources/go/kubernetes/pkg/generated/bindata.go':[ +9935, +10043, +10044, +], +'go-project:sources/go/kubernetes/pkg/kubectl/clusterrolebinding.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go':[ +46, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_cluster.go':[ +79, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_context.go':[ +73, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_pdb.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_secret.go':[ +113, +161, +163, +165, +166, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_service.go':[ +102, +254, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/replace.go':[ +83, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollingupdate.go':[ +98, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +90, +557, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_object_mapping.go':[ +259, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +247, +325, +401, +401, +401, +614, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/printing.go':[ +37, +67, +72, +], +'go-project:sources/go/kubernetes/pkg/kubectl/configmap.go':[ +58, +67, +84, +175, +], +'go-project:sources/go/kubernetes/pkg/kubectl/history.go':[ +104, +], +'go-project:sources/go/kubernetes/pkg/kubectl/metricsutil/metrics_client.go':[ +115, +], +'go-project:sources/go/kubernetes/pkg/kubectl/pdb.go':[ +42, +58, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolebinding.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/kubectl/run.go':[ +41, +45, +301, +856, +], +'go-project:sources/go/kubernetes/pkg/kubectl/scale.go':[ +147, +242, +], +'go-project:sources/go/kubernetes/pkg/kubectl/secret.go':[ +58, +67, +76, +175, +], +'go-project:sources/go/kubernetes/pkg/kubectl/service.go':[ +38, +64, +74, +75, +], +'go-project:sources/go/kubernetes/pkg/kubelet/certificate/certificate_store.go':[ +104, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go':[ +63, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/fake_client.go':[ +270, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/remote/docker_service.go':[ +221, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_network.go':[ +172, +374, +386, +393, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_node_status.go':[ +303, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +1435, +1446, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_container.go':[ +104, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/labels.go':[ +189, +223, +], +'go-project:sources/go/kubernetes/pkg/kubelet/lifecycle/predicate.go':[ +69, +106, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/cni/cni.go':[ +232, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/fake_iptables.go':[ +280, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport_manager.go':[ +119, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport_syncer.go':[ +231, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_unsupported.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/kubelet/remote/remote_runtime.go':[ +336, +337, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/image.go':[ +107, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +642, +1266, +1568, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/app-interface.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/imagestore.go':[ +45, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rktshim/pod-level-interface.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/httpstream.go':[ +236, +246, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/server.go':[ +316, +319, +639, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/cache/actual_state_of_world.go':[ +396, +], +'go-project:sources/go/kubernetes/pkg/master/master.go':[ +280, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +207, +208, +340, +631, +634, +755, +766, +768, +774, +837, +922, +989, +997, +999, +1182, +1217, +1398, +1442, +1446, +1449, +1450, +1452, +1670, +2367, +2600, +3230, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +61, +61, +209, +236, +245, +567, +1120, +1342, +1717, +1820, +], +'go-project:sources/go/kubernetes/pkg/printers/printers.go':[ +61, +72, +], +'go-project:sources/go/kubernetes/pkg/proxy/config/config.go':[ +120, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +528, +582, +582, +582, +582, +1201, +1268, +1455, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +223, +226, +236, +622, +765, +1027, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxysocket.go':[ +240, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +559, +], +'go-project:sources/go/kubernetes/pkg/registry/batch/job/strategy.go':[ +101, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/strategy.go':[ +186, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/controller/storage/storage.go':[ +67, +], +'go-project:sources/go/kubernetes/pkg/util/mount/nsenter_mount.go':[ +132, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder.go':[ +343, +], +'go-project:sources/go/kubernetes/pkg/volume/glusterfs/glusterfs.go':[ +983, +], +'go-project:sources/go/kubernetes/pkg/volume/nfs/nfs.go':[ +198, +], +'go-project:sources/go/kubernetes/pkg/volume/portworx/portworx_util.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd.go':[ +462, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd_util.go':[ +114, +139, +], +'go-project:sources/go/kubernetes/pkg/volume/scaleio/sio_volume.go':[ +387, +], +'go-project:sources/go/kubernetes/pkg/volume/storageos/storageos.go':[ +704, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go':[ +313, +], +'go-project:sources/go/kubernetes/pkg/volume/util/util.go':[ +141, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/noderestriction/admission.go':[ +126, +181, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer.go':[ +101, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go':[ +66, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/namespace_policy.go':[ +83, +90, +97, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go':[ +115, +168, +171, +171, +171, +171, +178, +178, +186, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +235, +254, +270, +412, +1187, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +236, +286, +530, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/testutil.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go':[ +180, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresource/strategy.go':[ +128, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/test/integration/testserver/resources.go':[ +42, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/unstructured/converter.go':[ +315, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/helper.go':[ +54, +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go':[ +169, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/http.go':[ +179, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go':[ +55, +60, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/watch/watch.go':[ +210, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/format.go':[ +31, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/legacy_audit.go':[ +115, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/errors.go':[ +33, +33, +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go':[ +78, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +136, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +419, +424, +433, +597, +601, +798, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/openapi/openapi.go':[ +139, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/matcher.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go':[ +250, +283, +291, +296, +301, +415, +774, +854, +857, +862, +866, +869, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi.go':[ +119, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/authentication.go':[ +55, +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go':[ +122, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +388, +865, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_helper.go':[ +120, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go':[ +190, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/watch_cache.go':[ +220, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go':[ +55, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +168, +169, +200, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +541, +560, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go':[ +142, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/portforward/portforward.go':[ +236, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/transport/round_trippers.go':[ +141, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/node.go':[ +97, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go':[ +228, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go':[ +264, +269, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/client/custom_metrics/client.go':[ +131, +], +'go-project:sources/go/kubernetes/test/e2e/addon_update.go':[ +288, +298, +301, +302, +321, +], +'go-project:sources/go/kubernetes/test/e2e/api/table_conversion.go':[ +49, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/cluster_size_autoscaling.go':[ +108, +127, +199, +200, +201, +303, +304, +383, +443, +459, +581, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/dns_autoscaling.go':[ +115, +144, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/es_utils.go':[ +59, +], +'go-project:sources/go/kubernetes/test/e2e/common/apparmor.go':[ +136, +], +'go-project:sources/go/kubernetes/test/e2e/common/configmap.go':[ +84, +85, +86, +94, +94, +98, +101, +106, +124, +125, +125, +149, +324, +378, +], +'go-project:sources/go/kubernetes/test/e2e/common/container_probe.go':[ +101, +108, +109, +155, +], +'go-project:sources/go/kubernetes/test/e2e/common/downward_api.go':[ +35, +], +'go-project:sources/go/kubernetes/test/e2e/common/downwardapi_volume.go':[ +43, +44, +46, +107, +214, +241, +], +'go-project:sources/go/kubernetes/test/e2e/common/empty_dir.go':[ +132, +138, +140, +147, +150, +154, +179, +], +'go-project:sources/go/kubernetes/test/e2e/common/expansion.go':[ +34, +43, +44, +89, +], +'go-project:sources/go/kubernetes/test/e2e/common/host_path.go':[ +51, +60, +68, +73, +74, +110, +111, +111, +], +'go-project:sources/go/kubernetes/test/e2e/common/init_container.go':[ +47, +48, +63, +64, +84, +89, +93, +291, +], +'go-project:sources/go/kubernetes/test/e2e/common/kubelet_etc_hosts.go':[ +60, +61, +129, +], +'go-project:sources/go/kubernetes/test/e2e/common/pods.go':[ +84, +148, +163, +173, +182, +185, +419, +459, +], +'go-project:sources/go/kubernetes/test/e2e/common/projected.go':[ +39, +79, +90, +91, +97, +105, +145, +167, +204, +204, +306, +306, +343, +368, +377, +433, +434, +435, +449, +454, +561, +791, +806, +807, +809, +870, +], +'go-project:sources/go/kubernetes/test/e2e/common/secrets.go':[ +38, +78, +89, +90, +96, +104, +127, +128, +150, +187, +187, +265, +265, +], +'go-project:sources/go/kubernetes/test/e2e/common/sysctl.go':[ +64, +73, +80, +], +'go-project:sources/go/kubernetes/test/e2e/common/volumes.go':[ +106, +181, +], +'go-project:sources/go/kubernetes/test/e2e/cronjob.go':[ +70, +71, +86, +123, +127, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_set.go':[ +120, +122, +156, +320, +382, +387, +396, +], +'go-project:sources/go/kubernetes/test/e2e/deployment.go':[ +286, +308, +371, +384, +388, +679, +982, +], +'go-project:sources/go/kubernetes/test/e2e/dns.go':[ +78, +334, +335, +338, +], +'go-project:sources/go/kubernetes/test/e2e/dns_common.go':[ +57, +175, +], +'go-project:sources/go/kubernetes/test/e2e/dns_configmap.go':[ +148, +], +'go-project:sources/go/kubernetes/test/e2e/empty_dir_wrapper.go':[ +67, +], +'go-project:sources/go/kubernetes/test/e2e/example_cluster_dns.go':[ +66, +], +'go-project:sources/go/kubernetes/test/e2e/examples.go':[ +97, +178, +467, +], +'go-project:sources/go/kubernetes/test/e2e/framework/exec_util.go':[ +112, +], +'go-project:sources/go/kubernetes/test/e2e/framework/firewall_util.go':[ +62, +175, +], +'go-project:sources/go/kubernetes/test/e2e/framework/google_compute.go':[ +53, +], +'go-project:sources/go/kubernetes/test/e2e/framework/ingress_utils.go':[ +386, +770, +824, +], +'go-project:sources/go/kubernetes/test/e2e/framework/jobs_util.go':[ +93, +], +'go-project:sources/go/kubernetes/test/e2e/framework/kubelet_stats.go':[ +297, +], +'go-project:sources/go/kubernetes/test/e2e/framework/metrics_util.go':[ +85, +], +'go-project:sources/go/kubernetes/test/e2e/framework/networking_utils.go':[ +646, +650, +699, +], +'go-project:sources/go/kubernetes/test/e2e/framework/nodes_util.go':[ +86, +87, +306, +], +'go-project:sources/go/kubernetes/test/e2e/framework/pv_util.go':[ +192, +242, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +203, +949, +], +'go-project:sources/go/kubernetes/test/e2e/framework/size.go':[ +44, +46, +46, +], +'go-project:sources/go/kubernetes/test/e2e/framework/statefulset_utils.go':[ +153, +433, +532, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +532, +829, +1508, +1508, +1983, +2034, +2481, +2509, +2983, +3116, +3992, +4236, +5188, +], +'go-project:sources/go/kubernetes/test/e2e/framework/volume_util.go':[ +219, +337, +], +'go-project:sources/go/kubernetes/test/e2e/garbage_collector.go':[ +54, +122, +138, +217, +219, +222, +242, +246, +279, +287, +295, +], +'go-project:sources/go/kubernetes/test/e2e/gke_node_pools.go':[ +30, +], +'go-project:sources/go/kubernetes/test/e2e/job.go':[ +43, +48, +], +'go-project:sources/go/kubernetes/test/e2e/kibana_logging.go':[ +32, +], +'go-project:sources/go/kubernetes/test/e2e/kube_proxy.go':[ +81, +103, +], +'go-project:sources/go/kubernetes/test/e2e/kubectl.go':[ +74, +110, +199, +211, +212, +213, +280, +290, +357, +435, +439, +445, +481, +481, +481, +485, +497, +571, +623, +624, +684, +702, +790, +798, +800, +800, +801, +802, +1120, +1130, +1141, +1339, +1496, +1500, +1588, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet.go':[ +152, +], +'go-project:sources/go/kubernetes/test/e2e/logging_soak.go':[ +34, +], +'go-project:sources/go/kubernetes/test/e2e/network_partition.go':[ +154, +236, +251, +253, +262, +264, +320, +], +'go-project:sources/go/kubernetes/test/e2e/network_policy.go':[ +50, +81, +98, +106, +120, +134, +331, +335, +], +'go-project:sources/go/kubernetes/test/e2e/networking.go':[ +96, +105, +117, +126, +], +'go-project:sources/go/kubernetes/test/e2e/no-snat.go':[ +47, +71, +], +'go-project:sources/go/kubernetes/test/e2e/nvidia-gpus.go':[ +91, +], +'go-project:sources/go/kubernetes/test/e2e/perf/load.go':[ +266, +], +'go-project:sources/go/kubernetes/test/e2e/podpreset.go':[ +104, +], +'go-project:sources/go/kubernetes/test/e2e/pods.go':[ +75, +], +'go-project:sources/go/kubernetes/test/e2e/portforward.go':[ +209, +212, +215, +220, +222, +226, +230, +231, +233, +236, +240, +247, +250, +252, +255, +258, +414, +478, +480, +], +'go-project:sources/go/kubernetes/test/e2e/proxy.go':[ +62, +66, +182, +184, +], +'go-project:sources/go/kubernetes/test/e2e/resource_quota.go':[ +47, +48, +53, +89, +202, +206, +377, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/opaque_resource.go':[ +75, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/predicates.go':[ +281, +305, +440, +488, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/priorities.go':[ +90, +90, +125, +], +'go-project:sources/go/kubernetes/test/e2e/security_context.go':[ +51, +115, +], +'go-project:sources/go/kubernetes/test/e2e/service.go':[ +82, +232, +298, +543, +546, +641, +645, +801, +832, +855, +862, +869, +1100, +1105, +1357, +1372, +1381, +], +'go-project:sources/go/kubernetes/test/e2e/service_accounts.go':[ +54, +59, +66, +72, +203, +205, +], +'go-project:sources/go/kubernetes/test/e2e/statefulset.go':[ +78, +93, +257, +262, +276, +282, +286, +412, +418, +466, +472, +891, +967, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pd.go':[ +79, +81, +90, +96, +98, +102, +106, +111, +113, +129, +330, +610, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes-gce.go':[ +131, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pv_reclaimpolicy.go':[ +122, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pvc_label_selector.go':[ +91, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volume_provisioning.go':[ +236, +264, +332, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volumes.go':[ +103, +129, +170, +239, +307, +477, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_placement.go':[ +106, +116, +322, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_vsan_policy.go':[ +109, +], +'go-project:sources/go/kubernetes/test/e2e/third-party.go':[ +86, +109, +113, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/ingress.go':[ +75, +], +'go-project:sources/go/kubernetes/test/e2e_federation/authn.go':[ +119, +], +'go-project:sources/go/kubernetes/test/e2e_federation/ingress.go':[ +351, +], +'go-project:sources/go/kubernetes/test/e2e_federation/namespace.go':[ +73, +], +'go-project:sources/go/kubernetes/test/e2e_federation/replicaset.go':[ +114, +], +'go-project:sources/go/kubernetes/test/e2e_federation/service.go':[ +72, +256, +], +'go-project:sources/go/kubernetes/test/e2e_node/environment/conformance.go':[ +140, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/internal_services.go':[ +78, +], +'go-project:sources/go/kubernetes/test/e2e_node/util.go':[ +132, +], +'go-project:sources/go/kubernetes/test/images/netexec/netexec.go':[ +299, +], +'go-project:sources/go/kubernetes/test/images/resource-consumer/controller/controller.go':[ +210, +], +'go-project:sources/go/kubernetes/test/soak/cauldron/cauldron.go':[ +131, +], +'go-project:sources/go/kubernetes/test/soak/serve_hostnames/serve_hostnames.go':[ +147, +149, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S122.json b/its/ruling/src/test/resources/expected/go/go-S122.json new file mode 100644 index 00000000..47425320 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S122.json @@ -0,0 +1,28 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1116.go':[ +11, +], +'go-project:ruling/src/test/resources/sources/go/S122.go':[ +8, +13, +17, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/noderestriction/admission.go':[ +144, +149, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +830, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/priorities.go':[ +277, +], +'go-project:sources/go/kubernetes/test/utils/runners.go':[ +284, +351, +418, +419, +514, +1025, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S126.json b/its/ruling/src/test/resources/expected/go/go-S126.json new file mode 100644 index 00000000..0907043a --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S126.json @@ -0,0 +1,543 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1862.go':[ +6, +13, +20, +], +'go-project:sources/go/kubernetes/cmd/gendocs/gen_kubectl_docs.go':[ +35, +], +'go-project:sources/go/kubernetes/cmd/genyaml/gen_kubectl_yaml.go':[ +54, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/certs.go':[ +283, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/preflight/checks.go':[ +601, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +475, +505, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +212, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +560, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/dns/dns.go':[ +259, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +544, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/sync/controller.go':[ +553, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +397, +], +'go-project:sources/go/kubernetes/pkg/api/testing/compat/compatibility_tester.go':[ +81, +], +'go-project:sources/go/kubernetes/pkg/api/v1/defaults.go':[ +120, +], +'go-project:sources/go/kubernetes/pkg/api/v1/resource/helpers.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/api/v1/validation/validation.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +1553, +1613, +1631, +1646, +1675, +1772, +2608, +2703, +2856, +2880, +2961, +2976, +3012, +3767, +4115, +4123, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/defaults.go':[ +62, +67, +415, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/validation/validation.go':[ +118, +474, +981, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/validation/validation.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +1516, +2598, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +55, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/instances.go':[ +72, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_instances.go':[ +80, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_loadbalancer.go':[ +617, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go':[ +415, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go':[ +165, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +326, +1768, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +306, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/cronjob_controller.go':[ +152, +223, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/sync.go':[ +85, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/util/deployment_util.go':[ +211, +228, +], +'go-project:sources/go/kubernetes/pkg/controller/disruption/disruption.go':[ +558, +562, +], +'go-project:sources/go/kubernetes/pkg/controller/job/jobcontroller.go':[ +594, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +603, +630, +898, +], +'go-project:sources/go/kubernetes/pkg/controller/node/taint_controller.go':[ +143, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/replica_calculator.go':[ +123, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set.go':[ +479, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set_utils.go':[ +113, +118, +], +'go-project:sources/go/kubernetes/pkg/controller/replication/replication_controller_utils.go':[ +123, +128, +], +'go-project:sources/go/kubernetes/pkg/controller/service/servicecontroller.go':[ +499, +687, +762, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_control.go':[ +286, +296, +367, +386, +473, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_utils.go':[ +221, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go':[ +113, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller.go':[ +324, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/keyring.go':[ +319, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/provider.go':[ +88, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/default_storage_factory_builder.go':[ +78, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/attach.go':[ +230, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/view.go':[ +120, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/get.go':[ +397, +506, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/proxy.go':[ +120, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollingupdate.go':[ +126, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_image.go':[ +166, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/top_node.go':[ +103, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/top_pod.go':[ +107, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/clientcache.go':[ +105, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +739, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/openapi/openapi.go':[ +223, +], +'go-project:sources/go/kubernetes/pkg/kubectl/deployment.go':[ +77, +165, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +296, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/config.go':[ +246, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/fake_runtime.go':[ +106, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +519, +1734, +1742, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_network.go':[ +73, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +161, +540, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/labels.go':[ +204, +211, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/fake_iptables.go':[ +307, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_linux.go':[ +508, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pleg/generic.go':[ +247, +320, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod_workers.go':[ +295, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +2487, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/httpstream.go':[ +229, +], +'go-project:sources/go/kubernetes/pkg/kubelet/status/status_manager.go':[ +315, +398, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/reconciler/reconciler.go':[ +235, +], +'go-project:sources/go/kubernetes/pkg/master/tunneler/ssh.go':[ +122, +211, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +1290, +3060, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +332, +340, +692, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +1190, +1205, +1222, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +578, +756, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/roundrobin.go':[ +101, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxier.go':[ +244, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/roundrobin.go':[ +101, +], +'go-project:sources/go/kubernetes/pkg/registry/core/namespace/storage/storage.go':[ +133, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/storage/eviction.go':[ +97, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +112, +144, +155, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/provider.go':[ +298, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/sysctl/mustmatchpatterns.go':[ +88, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/save_restore.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/version/verflag/verflag.go':[ +98, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/attacher.go':[ +176, +], +'go-project:sources/go/kubernetes/pkg/volume/downwardapi/downwardapi.go':[ +244, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/attacher.go':[ +110, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/util.go':[ +59, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/attacher.go':[ +165, +], +'go-project:sources/go/kubernetes/pkg/volume/glusterfs/glusterfs.go':[ +520, +621, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/attacher.go':[ +145, +], +'go-project:sources/go/kubernetes/pkg/volume/projected/projected.go':[ +286, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/attacher.go':[ +145, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/gcm.go':[ +91, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/influxdb.go':[ +52, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podnodeselector/admission.go':[ +128, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/admission.go':[ +107, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +232, +610, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/extender.go':[ +144, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/plugins.go':[ +135, +143, +233, +244, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/schedulercache/reconcile_affinity.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/validation/validation.go':[ +69, +75, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go':[ +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go':[ +353, +554, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/watch.go':[ +61, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +460, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +1575, +1592, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/validation.go':[ +51, +61, +298, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/json/fields.go':[ +378, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/errors.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/namer.go':[ +97, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/openapi/openapi.go':[ +85, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go':[ +416, +517, +687, +717, +779, +825, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/httplog/log.go':[ +191, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +526, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_helper.go':[ +211, +520, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/dial.go':[ +64, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go':[ +277, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/defaults.go':[ +120, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/resource/helpers.go':[ +96, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/client.go':[ +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/urlbackoff.go':[ +91, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go':[ +336, +519, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/expiration_cache.go':[ +132, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go':[ +216, +404, +420, +437, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/validation.go':[ +258, +264, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/jsonpath.go':[ +347, +387, +415, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/parser.go':[ +412, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apiserver/apiserver.go':[ +428, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_restart.go':[ +100, +], +'go-project:sources/go/kubernetes/test/e2e/dashboard.go':[ +80, +], +'go-project:sources/go/kubernetes/test/e2e/dns.go':[ +213, +], +'go-project:sources/go/kubernetes/test/e2e/framework/pv_util.go':[ +199, +241, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +5092, +], +'go-project:sources/go/kubernetes/test/e2e/logging_soak.go':[ +135, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +107, +], +'go-project:sources/go/kubernetes/test/e2e/ssh.go':[ +73, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_diskformat.go':[ +208, +], +'go-project:sources/go/kubernetes/test/e2e_federation/deployment.go':[ +174, +], +'go-project:sources/go/kubernetes/test/e2e_federation/ingress.go':[ +314, +], +'go-project:sources/go/kubernetes/test/e2e_federation/namespace.go':[ +221, +249, +], +'go-project:sources/go/kubernetes/test/e2e_federation/replicaset.go':[ +268, +], +'go-project:sources/go/kubernetes/test/e2e_federation/service.go':[ +352, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/ssh.go':[ +97, +], +'go-project:sources/go/kubernetes/test/images/netexec/netexec.go':[ +391, +], +'go-project:sources/go/kubernetes/test/utils/conditions.go':[ +69, +], +'go-project:sources/go/kubernetes/test/utils/runners.go':[ +671, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S131.json b/its/ruling/src/test/resources/expected/go/go-S131.json new file mode 100644 index 00000000..78c50186 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S131.json @@ -0,0 +1,895 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S108.go':[ +18, +], +'go-project:sources/go/kubernetes/cluster/images/etcd/rollback/rollback.go':[ +279, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/plugins.go':[ +131, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/server.go':[ +88, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/preflight/checks.go':[ +529, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/types/helpers.go':[ +57, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +349, +353, +785, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go':[ +154, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +224, +306, +444, +450, +597, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/package.go':[ +110, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +115, +132, +154, +182, +199, +204, +216, +229, +290, +306, +313, +333, +340, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/generators/openapi.go':[ +339, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/kubectl_dash_f.go':[ +57, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/util.go':[ +150, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/stubs/route53api.go':[ +82, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/rrchangeset.go':[ +82, +], +'go-project:sources/go/kubernetes/federation/pkg/federatedtypes/crudtester/crudtester.go':[ +161, +184, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +775, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +289, +552, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/sync/controller.go':[ +264, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/clusterselector/clusterselector.go':[ +65, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_updater.go':[ +110, +], +'go-project:sources/go/kubernetes/pkg/api/helper/helpers.go':[ +323, +], +'go-project:sources/go/kubernetes/pkg/api/persistentvolume/util.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/api/pod/util.go':[ +52, +134, +], +'go-project:sources/go/kubernetes/pkg/api/resource/helpers.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +484, +694, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +37, +39, +44, +50, +55, +61, +66, +72, +77, +83, +88, +94, +99, +105, +110, +116, +121, +], +'go-project:sources/go/kubernetes/pkg/api/v1/helper/helpers.go':[ +140, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +35, +140, +222, +], +'go-project:sources/go/kubernetes/pkg/api/v1/resource/helpers.go':[ +151, +], +'go-project:sources/go/kubernetes/pkg/api/v1/validation/validation.go':[ +127, +], +'go-project:sources/go/kubernetes/pkg/api/validation/schema.go':[ +102, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +1715, +2813, +4167, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/helpers.go':[ +231, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1alpha1/defaults.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/v1beta1/defaults.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/client/conditions/conditions.go':[ +40, +44, +46, +59, +63, +65, +77, +81, +83, +96, +100, +116, +120, +156, +160, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/generic.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/generic.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/conditions.go':[ +157, +161, +163, +176, +180, +182, +194, +198, +200, +213, +217, +233, +237, +273, +277, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +2315, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +973, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +737, +1007, +1125, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/deployment_controller.go':[ +684, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/progress.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/sync.go':[ +325, +440, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/garbagecollector.go':[ +222, +298, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/graph_builder.go':[ +474, +], +'go-project:sources/go/kubernetes/pkg/controller/namespace/deletion/namespaced_resources_deleter.go':[ +539, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +801, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/serviceaccounts_controller.go':[ +202, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_pod_control.go':[ +188, +], +'go-project:sources/go/kubernetes/pkg/fieldpath/fieldpath.go':[ +45, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/authenticator/config.go':[ +202, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply.go':[ +575, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go':[ +225, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_view_last_applied.go':[ +138, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/attach.go':[ +149, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/navigation_step_parser.go':[ +45, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/set.go':[ +138, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/drain.go':[ +270, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/patch.go':[ +123, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollingupdate.go':[ +401, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/run.go':[ +215, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/taint.go':[ +226, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/testdata/edit/record.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/testing/fake.go':[ +419, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/factory_client_access.go':[ +494, +587, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +210, +223, +226, +265, +628, +749, +], +'go-project:sources/go/kubernetes/pkg/kubectl/history.go':[ +55, +], +'go-project:sources/go/kubernetes/pkg/kubectl/proxy_server.go':[ +222, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/result.go':[ +270, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource_filter.go':[ +48, +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolebinding.go':[ +119, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rollback.go':[ +56, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +436, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rollout_status.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/kubectl/scale.go':[ +54, +128, +], +'go-project:sources/go/kubernetes/pkg/kubectl/service.go':[ +157, +], +'go-project:sources/go/kubernetes/pkg/kubectl/sorting_printer.go':[ +85, +100, +], +'go-project:sources/go/kubernetes/pkg/kubectl/stop.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go':[ +123, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_linux.go':[ +334, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/node_container_manager.go':[ +215, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/pod_container_manager_linux.go':[ +99, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/qos_container_manager_linux.go':[ +273, +294, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/file_linux.go':[ +111, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/helpers.go':[ +259, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go':[ +649, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_service.go':[ +467, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/fake_client.go':[ +138, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/helpers.go':[ +804, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +340, +1178, +1880, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_node_status.go':[ +1000, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +396, +449, +542, +650, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/helpers.go':[ +75, +91, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_container.go':[ +551, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/cni/cni.go':[ +100, +], +'go-project:sources/go/kubernetes/pkg/kubelet/prober/worker.go':[ +85, +], +'go-project:sources/go/kubernetes/pkg/kubelet/qos/policy.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +701, +1672, +1903, +2307, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/auth.go':[ +68, +99, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/httpstream.go':[ +193, +282, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/remotecommand/httpstream.go':[ +151, +], +'go-project:sources/go/kubernetes/pkg/printers/customcolumn.go':[ +205, +], +'go-project:sources/go/kubernetes/pkg/printers/humanreadable.go':[ +405, +833, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +1003, +1223, +1248, +1306, +1801, +2852, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +281, +704, +], +'go-project:sources/go/kubernetes/pkg/printers/json.go':[ +40, +84, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxysocket.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +87, +], +'go-project:sources/go/kubernetes/pkg/quota/evaluator/core/pods.go':[ +228, +], +'go-project:sources/go/kubernetes/pkg/quota/evaluator/core/services.go':[ +139, +162, +171, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/allocator/storage/storage.go':[ +199, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +299, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_role.go':[ +188, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go':[ +206, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rest/storage_rbac.go':[ +172, +200, +231, +261, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/util/util.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/serviceaccount/jwt.go':[ +220, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/iptables.go':[ +604, +], +'go-project:sources/go/kubernetes/pkg/util/mount/mount_linux.go':[ +344, +], +'go-project:sources/go/kubernetes/pkg/util/term/term_writer.go':[ +56, +], +'go-project:sources/go/kubernetes/pkg/util/version/version.go':[ +157, +173, +], +'go-project:sources/go/kubernetes/pkg/volume/glusterfs/glusterfs.go':[ +245, +], +'go-project:sources/go/kubernetes/pkg/volume/nfs/nfs.go':[ +196, +], +'go-project:sources/go/kubernetes/pkg/volume/quobyte/quobyte.go':[ +451, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd.go':[ +203, +], +'go-project:sources/go/kubernetes/pkg/volume/storageos/storageos.go':[ +183, +454, +731, +], +'go-project:sources/go/kubernetes/pkg/volume/util.go':[ +103, +108, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume_util.go':[ +203, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialization/initialization.go':[ +140, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/limitranger/admission.go':[ +403, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/node/node_authorizer.go':[ +82, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/examples/client-go/client/cr.go':[ +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go':[ +303, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions/generic.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/informers/internalversion/generic.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go':[ +87, +331, +428, +438, +460, +463, +473, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/errors.go':[ +34, +54, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go':[ +159, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/amount.go':[ +228, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/math.go':[ +62, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go':[ +165, +251, +296, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/suffix.go':[ +89, +138, +156, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go':[ +328, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/cloner.go':[ +140, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +692, +731, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/queryparams/convert.go':[ +173, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/unstructured/converter.go':[ +158, +160, +166, +173, +180, +611, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/fields/selector.go':[ +339, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +256, +275, +285, +392, +525, +538, +606, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/helper.go':[ +158, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go':[ +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/recognizer/recognizer.go':[ +67, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go':[ +183, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/errors/errors.go':[ +173, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go':[ +162, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/intstr/intstr.go':[ +165, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/json/json.go':[ +63, +83, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/jsonmergepatch/patch.go':[ +113, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/http.go':[ +67, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +733, +1127, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/reflect/deep_equal.go':[ +117, +260, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go':[ +166, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate.go':[ +279, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go':[ +273, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go':[ +42, +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +671, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +279, +634, +897, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go':[ +186, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/decorated_watcher.go':[ +52, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +367, +647, +663, +690, +706, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/rest/response_checker.go':[ +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/config.go':[ +262, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/handler.go':[ +134, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi.go':[ +226, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +861, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_watcher.go':[ +409, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go':[ +301, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/wsstream/conn.go':[ +202, +277, +326, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/generic.go':[ +68, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/helper/helpers.go':[ +323, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +37, +39, +44, +50, +55, +61, +66, +72, +77, +83, +88, +94, +99, +105, +110, +116, +121, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/resource/helpers.go':[ +151, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/helpers.go':[ +230, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1alpha1/defaults.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/v1beta1/defaults.go':[ +39, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +142, +942, +956, +1123, +1161, +1196, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/exec.go':[ +48, +59, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/funcs.go':[ +89, +161, +285, +558, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/controller.go':[ +300, +367, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/shared_informer.go':[ +342, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/store.go':[ +97, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/testing/fake_controller_source.go':[ +137, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/merged_client_builder.go':[ +107, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/validation.go':[ +61, +96, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/portforward/portforward.go':[ +280, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/event.go':[ +280, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/remotecommand.go':[ +163, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/transport/round_trippers.go':[ +43, +64, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/cert/pem.go':[ +83, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/parser.go':[ +254, +339, +377, +424, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/externalversions/generic.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/informers/internalversion/generic.go':[ +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/controllers/autoregister/autoregister_controller.go':[ +229, +], +'go-project:sources/go/kubernetes/test/e2e/audit.go':[ +157, +], +'go-project:sources/go/kubernetes/test/e2e/common/pods.go':[ +242, +], +'go-project:sources/go/kubernetes/test/e2e/deployment.go':[ +1195, +], +'go-project:sources/go/kubernetes/test/e2e/e2e.go':[ +67, +141, +], +'go-project:sources/go/kubernetes/test/e2e/framework/jobs_util.go':[ +89, +], +'go-project:sources/go/kubernetes/test/e2e/framework/metrics_util.go':[ +263, +318, +466, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +1338, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +1204, +1243, +1307, +1333, +1351, +1438, +1442, +2157, +], +'go-project:sources/go/kubernetes/test/e2e/ha_master.go':[ +157, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +429, +], +'go-project:sources/go/kubernetes/test/e2e/resource_quota.go':[ +554, +], +'go-project:sources/go/kubernetes/test/e2e/stackdriver_monitoring.go':[ +123, +], +'go-project:sources/go/kubernetes/test/e2e/statefulset.go':[ +811, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volume_provisioning.go':[ +757, +], +'go-project:sources/go/kubernetes/test/e2e/upgrades/sysctl.go':[ +54, +], +'go-project:sources/go/kubernetes/test/e2e_node/image_list.go':[ +100, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/node_e2e.go':[ +175, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/kernel_validator.go':[ +122, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/package_validator.go':[ +258, +297, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/validators.go':[ +67, +], +'go-project:sources/go/kubernetes/test/list/main.go':[ +186, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/repair.go':[ +47, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/repair.go':[ +47, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/reflect/deep_equal.go':[ +117, +260, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/exec.go':[ +48, +59, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/funcs.go':[ +89, +161, +285, +558, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1313.json b/its/ruling/src/test/resources/expected/go/go-S1313.json new file mode 100644 index 00000000..c921e371 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1313.json @@ -0,0 +1,83 @@ +{ +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/v1alpha1/defaults.go':[ +28, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/tests/commontests.go':[ +32, +37, +55, +59, +80, +84, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/testing/testing.go':[ +84, +125, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/defaults.go':[ +307, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_zones.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer.go':[ +59, +59, +59, +59, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/metadata.go':[ +39, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/cri/testing/fake_runtime_service.go':[ +32, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/fake_client.go':[ +560, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_linux.go':[ +123, +], +'go-project:sources/go/kubernetes/pkg/kubemark/hollow_proxy.go':[ +90, +], +'go-project:sources/go/kubernetes/pkg/master/services.go':[ +34, +], +'go-project:sources/go/kubernetes/test/e2e/dns_configmap.go':[ +148, +149, +150, +165, +170, +175, +], +'go-project:sources/go/kubernetes/test/e2e/firewall.go':[ +53, +], +'go-project:sources/go/kubernetes/test/e2e/framework/firewall_util.go':[ +162, +184, +], +'go-project:sources/go/kubernetes/test/e2e/framework/test_context.go':[ +224, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +183, +], +'go-project:sources/go/kubernetes/test/e2e/networking.go':[ +51, +], +'go-project:sources/go/kubernetes/test/e2e/service.go':[ +1300, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/apiserver.go':[ +28, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/kubelet.go':[ +145, +], +'go-project:sources/go/kubernetes/test/integration/framework/master_utils.go':[ +410, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1314.json b/its/ruling/src/test/resources/expected/go/go-S1314.json new file mode 100644 index 00000000..beffdd7d --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1314.json @@ -0,0 +1,5 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1314.go':[ +6, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S134.json b/its/ruling/src/test/resources/expected/go/go-S134.json new file mode 100644 index 00000000..f2fc16ce --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S134.json @@ -0,0 +1,285 @@ +{ +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/client_generator.go':[ +289, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +312, +322, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +203, +232, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/links.go':[ +228, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +227, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/rrchangeset.go':[ +99, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/cluster/clustercontroller.go':[ +199, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +834, +841, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/dns/dns.go':[ +268, +424, +429, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/podanalyzer/pod_helper.go':[ +65, +69, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/util/util.go':[ +312, +315, +], +'go-project:sources/go/kubernetes/pkg/api/pod/util.go':[ +68, +138, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +156, +226, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +888, +911, +930, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/validation/validation.go':[ +957, +961, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/validation/validation.go':[ +51, +55, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +1514, +3186, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_loadbalancer.go':[ +361, +384, +402, +422, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_instances.go':[ +556, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +799, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/photon/photon.go':[ +196, +327, +389, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +487, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +242, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +930, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +598, +610, +625, +637, +652, +655, +659, +689, +747, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go':[ +261, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller.go':[ +321, +348, +546, +566, +576, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/set.go':[ +224, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_role.go':[ +254, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/drain.go':[ +641, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_image.go':[ +189, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper.go':[ +88, +], +'go-project:sources/go/kubernetes/pkg/kubectl/configmap.go':[ +193, +], +'go-project:sources/go/kubernetes/pkg/kubectl/secret.go':[ +192, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go':[ +203, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +343, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +461, +496, +564, +568, +579, +591, +595, +606, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/fake_iptables.go':[ +272, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/reconciler/reconciler.go':[ +296, +303, +], +'go-project:sources/go/kubernetes/pkg/master/controller.go':[ +372, +], +'go-project:sources/go/kubernetes/pkg/printers/humanreadable.go':[ +329, +338, +352, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +221, +225, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +1234, +1332, +1343, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +273, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +378, +498, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/policy_comparator.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/rule.go':[ +157, +163, +], +'go-project:sources/go/kubernetes/pkg/util/ipconfig/ipconfig.go':[ +80, +83, +], +'go-project:sources/go/kubernetes/pkg/util/oom/oom_linux.go':[ +108, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_common.go':[ +292, +299, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd_util.go':[ +159, +], +'go-project:sources/go/kubernetes/pkg/volume/util.go':[ +115, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/limitranger/admission.go':[ +472, +477, +482, +490, +495, +500, +521, +530, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/controller.go':[ +356, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +553, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go':[ +332, +336, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go':[ +245, +248, +254, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/interface.go':[ +175, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +1430, +1596, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/json/fields.go':[ +194, +205, +406, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/response.go':[ +128, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +811, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +371, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/transport.go':[ +180, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/reflector.go':[ +312, +], +'go-project:sources/go/kubernetes/test/e2e/framework/resource_usage_gatherer.go':[ +332, +342, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +1039, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +3981, +3987, +4005, +4008, +4173, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +580, +587, +681, +698, +], +'go-project:sources/go/kubernetes/test/e2e/stackdriver_monitoring.go':[ +130, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/server.go':[ +241, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S138.json b/its/ruling/src/test/resources/expected/go/go-S138.json new file mode 100644 index 00000000..26090f8c --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S138.json @@ -0,0 +1,361 @@ +{ +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +652, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/server.go':[ +420, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/completion.go':[ +137, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/certs.go':[ +43, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/options/options.go':[ +148, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +362, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +740, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go':[ +121, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/server.go':[ +88, +292, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/deployment/deploymentcontroller.go':[ +417, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +123, +677, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/replicaset/replicasetcontroller.go':[ +395, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +257, +], +'go-project:sources/go/kubernetes/pkg/api/testapi/testapi.go':[ +103, +], +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +131, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +396, +1193, +2805, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/defaults.go':[ +194, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +2610, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +65, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_loadbalancer.go':[ +115, +501, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go':[ +45, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +598, +1227, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +750, +1287, +1288, +], +'go-project:sources/go/kubernetes/pkg/controller/endpoint/endpoints_controller.go':[ +299, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +199, +516, +829, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/horizontal.go':[ +359, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_control.go':[ +242, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply.go':[ +184, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go':[ +91, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/completion.go':[ +142, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/expose.go':[ +123, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/get.go':[ +148, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/patch.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollingupdate.go':[ +142, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/run.go':[ +139, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +153, +156, +], +'go-project:sources/go/kubernetes/pkg/kubectl/service.go':[ +79, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/eviction_manager.go':[ +214, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +295, +1447, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +424, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go':[ +554, +], +'go-project:sources/go/kubernetes/pkg/printers/humanreadable.go':[ +281, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +986, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rest/storage_rbac.go':[ +134, +137, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_provision.go':[ +68, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/controller_policy.go':[ +60, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/policy.go':[ +137, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go':[ +55, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +613, +645, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +191, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +131, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/conversion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/config.go':[ +156, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/cluster_size_autoscaling.go':[ +70, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/dns_autoscaling.go':[ +44, +], +'go-project:sources/go/kubernetes/test/e2e/common/configmap.go':[ +33, +157, +], +'go-project:sources/go/kubernetes/test/e2e/common/container_probe.go':[ +42, +], +'go-project:sources/go/kubernetes/test/e2e/common/downward_api.go':[ +31, +], +'go-project:sources/go/kubernetes/test/e2e/common/downwardapi_volume.go':[ +33, +], +'go-project:sources/go/kubernetes/test/e2e/common/host_path.go':[ +34, +], +'go-project:sources/go/kubernetes/test/e2e/common/init_container.go':[ +37, +200, +], +'go-project:sources/go/kubernetes/test/e2e/common/pods.go':[ +123, +], +'go-project:sources/go/kubernetes/test/e2e/common/projected.go':[ +34, +173, +511, +], +'go-project:sources/go/kubernetes/test/e2e/common/secrets.go':[ +34, +156, +], +'go-project:sources/go/kubernetes/test/e2e/common/sysctl.go':[ +31, +], +'go-project:sources/go/kubernetes/test/e2e/common/volumes.go':[ +56, +], +'go-project:sources/go/kubernetes/test/e2e/cronjob.go':[ +55, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_set.go':[ +65, +], +'go-project:sources/go/kubernetes/test/e2e/disruption.go':[ +44, +], +'go-project:sources/go/kubernetes/test/e2e/dns.go':[ +314, +], +'go-project:sources/go/kubernetes/test/e2e/examples.go':[ +51, +], +'go-project:sources/go/kubernetes/test/e2e/extension/initializers.go':[ +37, +], +'go-project:sources/go/kubernetes/test/e2e/firewall.go':[ +34, +], +'go-project:sources/go/kubernetes/test/e2e/framework/firewall_util.go':[ +154, +], +'go-project:sources/go/kubernetes/test/e2e/garbage_collector.go':[ +211, +], +'go-project:sources/go/kubernetes/test/e2e/kube_proxy.go':[ +39, +49, +], +'go-project:sources/go/kubernetes/test/e2e/kubectl.go':[ +232, +340, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet.go':[ +286, +], +'go-project:sources/go/kubernetes/test/e2e/network_partition.go':[ +97, +452, +453, +466, +], +'go-project:sources/go/kubernetes/test/e2e/network_policy.go':[ +41, +], +'go-project:sources/go/kubernetes/test/e2e/networking.go':[ +30, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +298, +447, +], +'go-project:sources/go/kubernetes/test/e2e/perf/load.go':[ +80, +], +'go-project:sources/go/kubernetes/test/e2e/podpreset.go':[ +36, +], +'go-project:sources/go/kubernetes/test/e2e/pods.go':[ +40, +43, +49, +], +'go-project:sources/go/kubernetes/test/e2e/proxy.go':[ +52, +54, +72, +], +'go-project:sources/go/kubernetes/test/e2e/resource_quota.go':[ +43, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/opaque_resource.go':[ +36, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/predicates.go':[ +55, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/priorities.go':[ +60, +], +'go-project:sources/go/kubernetes/test/e2e/service.go':[ +42, +470, +1024, +1326, +], +'go-project:sources/go/kubernetes/test/e2e/service_accounts.go':[ +40, +245, +], +'go-project:sources/go/kubernetes/test/e2e/statefulset.go':[ +53, +63, +361, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pd.go':[ +51, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes.go':[ +100, +126, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volume_provisioning.go':[ +214, +226, +227, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volumes.go':[ +83, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_placement.go':[ +35, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_vsan_policy.go':[ +86, +], +'go-project:sources/go/kubernetes/test/e2e_federation/service.go':[ +48, +77, +], +'go-project:sources/go/kubernetes/test/e2e_node/node_problem_detector_linux.go':[ +43, +66, +97, +], +'go-project:sources/go/kubernetes/test/e2e_node/runner/remote/run_remote.go':[ +161, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/server.go':[ +146, +], +'go-project:sources/go/kubernetes/test/soak/cauldron/cauldron.go':[ +59, +], +'go-project:sources/go/kubernetes/test/soak/serve_hostnames/serve_hostnames.go':[ +64, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1451.json b/its/ruling/src/test/resources/expected/go/go-S1451.json new file mode 100644 index 00000000..d7ea07e8 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1451.json @@ -0,0 +1,80 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S103.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S105.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S107.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1110.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1116.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1125.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1135.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1192.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S122.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1656.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1763.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S1862.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S2068.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S2757.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S3923.go':[ +0, +], +'go-project:ruling/src/test/resources/sources/go/S4663.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/netutil/addr.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/reflect/type.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/exec.go':[ +0, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/funcs.go':[ +0, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/expansion/expand.go':[ +0, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/reflect/type.go':[ +0, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/exec.go':[ +0, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/funcs.go':[ +0, +], +'go-project:sources/go/kubernetes/third_party/forked/gonum/graph/simple/directed_acyclic.go':[ +0, +], +'go-project:sources/go/kubernetes/third_party/forked/gonum/graph/simple/edgeholder.go':[ +0, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1479.json b/its/ruling/src/test/resources/expected/go/go-S1479.json new file mode 100644 index 00000000..528ce9d6 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1479.json @@ -0,0 +1,14 @@ +{ +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/externalversions/generic.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/client/informers/informers_generated/internalversion/generic.go':[ +64, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/metrics/metrics.go':[ +165, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/informers/generic.go':[ +68, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1656.json b/its/ruling/src/test/resources/expected/go/go-S1656.json new file mode 100644 index 00000000..0a45ed79 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1656.json @@ -0,0 +1,6 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1656.go':[ +7, +9, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1763.json b/its/ruling/src/test/resources/expected/go/go-S1763.json new file mode 100644 index 00000000..55620698 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1763.json @@ -0,0 +1,8 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1763.go':[ +8, +19, +23, +27, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1764.json b/its/ruling/src/test/resources/expected/go/go-S1764.json new file mode 100644 index 00000000..60782905 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1764.json @@ -0,0 +1,5 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S122.go':[ +10, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1821.json b/its/ruling/src/test/resources/expected/go/go-S1821.json new file mode 100644 index 00000000..dfef7aac --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1821.json @@ -0,0 +1,196 @@ +{ +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +353, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +309, +450, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +132, +189, +199, +204, +216, +229, +313, +340, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +39, +44, +50, +55, +61, +66, +72, +77, +83, +88, +94, +99, +105, +110, +116, +121, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/helpers.go':[ +231, +], +'go-project:sources/go/kubernetes/pkg/client/conditions/conditions.go':[ +46, +65, +83, +102, +122, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/conditions.go':[ +163, +182, +200, +219, +239, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +1131, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/garbagecollector.go':[ +380, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/tokens_controller.go':[ +301, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +154, +156, +212, +226, +], +'go-project:sources/go/kubernetes/pkg/kubectl/sorting_printer.go':[ +212, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +1012, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pleg/generic.go':[ +155, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +244, +], +'go-project:sources/go/kubernetes/pkg/printers/jsonpath.go':[ +49, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go':[ +127, +], +'go-project:sources/go/kubernetes/pkg/serviceaccount/jwt.go':[ +148, +], +'go-project:sources/go/kubernetes/pkg/volume/util.go':[ +108, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/noderestriction/admission.go':[ +101, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/errors/errors.go':[ +463, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/help.go':[ +134, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/amount.go':[ +122, +138, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go':[ +302, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go':[ +72, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go':[ +336, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +642, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/unstructured/converter.go':[ +158, +160, +166, +173, +180, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +574, +705, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go':[ +154, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/jsonmergepatch/patch.go':[ +117, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go':[ +95, +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +2084, +2091, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/validation/field/errors.go':[ +63, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/response.go':[ +125, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/status.go':[ +42, +53, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +671, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +634, +905, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/watcher.go':[ +301, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +39, +44, +50, +55, +61, +66, +72, +77, +83, +88, +94, +99, +105, +110, +116, +121, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/helpers.go':[ +230, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/funcs.go':[ +122, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/repair.go':[ +47, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/repair.go':[ +47, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/funcs.go':[ +122, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1862.json b/its/ruling/src/test/resources/expected/go/go-S1862.json new file mode 100644 index 00000000..fa754343 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1862.json @@ -0,0 +1,10 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S1862.go':[ +6, +13, +20, +28, +30, +37, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1871.json b/its/ruling/src/test/resources/expected/go/go-S1871.json new file mode 100644 index 00000000..67198497 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1871.json @@ -0,0 +1,15 @@ +{ +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +490, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +532, +535, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/reflect/deep_equal.go':[ +331, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/reflect/deep_equal.go':[ +331, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S1940.json b/its/ruling/src/test/resources/expected/go/go-S1940.json new file mode 100644 index 00000000..38bbb4c2 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S1940.json @@ -0,0 +1,11 @@ +{ +'go-project:sources/go/kubernetes/cmd/genman/gen_kube_man.go':[ +135, +], +'go-project:sources/go/kubernetes/cmd/genyaml/gen_kubectl_yaml.go':[ +92, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_vsan_policy.go':[ +99, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S2068.json b/its/ruling/src/test/resources/expected/go/go-S2068.json new file mode 100644 index 00000000..b4e2e95f --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S2068.json @@ -0,0 +1,15 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S2068.go':[ +4, +9, +10, +32, +], +'go-project:sources/go/kubernetes/pkg/generated/bindata.go':[ +209, +3686, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_secret.go':[ +148, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S2757.json b/its/ruling/src/test/resources/expected/go/go-S2757.json new file mode 100644 index 00000000..860536b7 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S2757.json @@ -0,0 +1,6 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S2757.go':[ +6, +7, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S3776.json b/its/ruling/src/test/resources/expected/go/go-S3776.json new file mode 100644 index 00000000..cafc684d --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S3776.json @@ -0,0 +1,1919 @@ +{ +'go-project:sources/go/kubernetes/cluster/addons/fluentd-elasticsearch/es-image/elasticsearch_logging_discovery.go':[ +43, +], +'go-project:sources/go/kubernetes/cluster/images/etcd-version-monitor/etcd-version-monitor.go':[ +142, +], +'go-project:sources/go/kubernetes/cluster/images/etcd/rollback/rollback.go':[ +57, +236, +], +'go-project:sources/go/kubernetes/cmd/genman/gen_kube_man.go':[ +39, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/hyperkube.go':[ +118, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +110, +335, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/controllermanager.go':[ +106, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/server.go':[ +420, +605, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/init.go':[ +215, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/token.go':[ +233, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/discovery/token/token.go':[ +39, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/certs/certs.go':[ +43, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/token/bootstrap.go':[ +41, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/bootstrap.go':[ +45, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +190, +362, +747, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/client_generator.go':[ +263, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/generator_fake_for_type.go':[ +88, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/generator_for_type.go':[ +77, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +137, +186, +666, +740, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/cmd.go':[ +121, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +293, +469, +615, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/namer.go':[ +105, +175, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/package.go':[ +145, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +153, +181, +378, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/packages.go':[ +103, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/lister-gen/generators/lister.go':[ +70, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/generators/openapi.go':[ +337, +], +'go-project:sources/go/kubernetes/cmd/linkcheck/links.go':[ +74, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/example_syncer.go':[ +48, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/links.go':[ +38, +172, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/mungedocs.go':[ +93, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/util.go':[ +33, +81, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/server.go':[ +88, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +221, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/coredns/rrchangeset.go':[ +72, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/google/clouddns/rrchangeset.go':[ +52, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/tests/commontests.go':[ +121, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/cluster/clustercontroller.go':[ +161, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/deployment/deploymentcontroller.go':[ +355, +417, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +456, +606, +677, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/namespace/namespace_controller.go':[ +296, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/replicaset/replicasetcontroller.go':[ +395, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/dns/dns.go':[ +229, +361, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +404, +529, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/sync/controller.go':[ +514, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/cluster_util.go':[ +44, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/deployment.go':[ +29, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_informer.go':[ +135, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_updater.go':[ +93, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/planner/planner.go':[ +67, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/podanalyzer/pod_helper.go':[ +48, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +257, +521, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/join.go':[ +177, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/util/util.go':[ +282, +], +'go-project:sources/go/kubernetes/pkg/api/endpoints/util.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/api/helper/qos/qos.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/api/persistentvolume/util.go':[ +33, +], +'go-project:sources/go/kubernetes/pkg/api/pod/util.go':[ +33, +120, +], +'go-project:sources/go/kubernetes/pkg/api/ref/ref.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/api/resource/helpers.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/api/testapi/testapi.go':[ +103, +], +'go-project:sources/go/kubernetes/pkg/api/testing/conversion.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +96, +503, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +35, +131, +], +'go-project:sources/go/kubernetes/pkg/api/v1/defaults.go':[ +126, +300, +], +'go-project:sources/go/kubernetes/pkg/api/v1/endpoints/util.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/api/v1/helper/qos/qos.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +120, +208, +], +'go-project:sources/go/kubernetes/pkg/api/v1/ref/ref.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/api/v1/resource/helpers.go':[ +30, +77, +], +'go-project:sources/go/kubernetes/pkg/api/validation/schema.go':[ +156, +243, +364, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +396, +870, +1193, +1522, +1976, +2196, +2616, +2665, +2805, +3480, +3616, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/validation/validation.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/conversion.go':[ +87, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/defaults.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/conversion.go':[ +45, +116, +], +'go-project:sources/go/kubernetes/pkg/apis/componentconfig/v1alpha1/defaults.go':[ +56, +194, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/validation/validation.go':[ +500, +943, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/validation/validation.go':[ +37, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/helpers.go':[ +207, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/validation/validation.go':[ +190, +], +'go-project:sources/go/kubernetes/pkg/auth/authorizer/abac/abac.go':[ +54, +132, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/clientset.go':[ +419, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/clientset.go':[ +199, +], +'go-project:sources/go/kubernetes/pkg/client/conditions/conditions.go':[ +114, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/conditions.go':[ +231, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/testclient/simple/simple_testclient.go':[ +116, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +803, +951, +1240, +1383, +1506, +1527, +1682, +1854, +2015, +2149, +2210, +2270, +2399, +2469, +2610, +2989, +3121, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +65, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_routes.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure.go':[ +204, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_blobDiskController.go':[ +374, +565, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_controllerCommon.go':[ +144, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_loadbalancer.go':[ +39, +115, +294, +343, +501, +747, +873, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_routes.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/azure/azure_storage.go':[ +26, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/cloudstack/cloudstack_loadbalancer.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce.go':[ +170, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_instances.go':[ +195, +341, +505, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_external.go':[ +45, +412, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_loadbalancer_internal.go':[ +39, +364, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_op.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_routes.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/mesos/client.go':[ +199, +273, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack.go':[ +578, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/openstack/openstack_loadbalancer.go':[ +306, +598, +954, +1068, +1227, +1376, +1438, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/photon/photon.go':[ +170, +310, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +471, +750, +1047, +1287, +1569, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere_util.go':[ +199, +], +'go-project:sources/go/kubernetes/pkg/controller/client_builder.go':[ +125, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +144, +200, +267, +], +'go-project:sources/go/kubernetes/pkg/controller/controller_ref_manager.go':[ +80, +], +'go-project:sources/go/kubernetes/pkg/controller/controller_utils.go':[ +919, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/cronjob_controller.go':[ +204, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/daemoncontroller.go':[ +471, +714, +899, +1037, +], +'go-project:sources/go/kubernetes/pkg/controller/daemon/update.go':[ +85, +152, +212, +400, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/deployment_controller.go':[ +560, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/progress.go':[ +80, +], +'go-project:sources/go/kubernetes/pkg/controller/deployment/sync.go':[ +230, +395, +], +'go-project:sources/go/kubernetes/pkg/controller/disruption/disruption.go':[ +537, +], +'go-project:sources/go/kubernetes/pkg/controller/endpoint/endpoints_controller.go':[ +299, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/garbagecollector.go':[ +287, +], +'go-project:sources/go/kubernetes/pkg/controller/garbagecollector/graph_builder.go':[ +406, +454, +], +'go-project:sources/go/kubernetes/pkg/controller/job/jobcontroller.go':[ +386, +553, +], +'go-project:sources/go/kubernetes/pkg/controller/namespace/deletion/namespaced_resources_deleter.go':[ +98, +163, +], +'go-project:sources/go/kubernetes/pkg/controller/node/controller_utils.go':[ +49, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +199, +516, +702, +829, +], +'go-project:sources/go/kubernetes/pkg/controller/node/range_allocator.go':[ +65, +222, +], +'go-project:sources/go/kubernetes/pkg/controller/node/testutil/test_utils.go':[ +269, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/horizontal.go':[ +213, +359, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/metrics/legacy_metrics_client.go':[ +181, +], +'go-project:sources/go/kubernetes/pkg/controller/podautoscaler/replica_calculator.go':[ +48, +181, +], +'go-project:sources/go/kubernetes/pkg/controller/replicaset/replica_set.go':[ +272, +433, +534, +], +'go-project:sources/go/kubernetes/pkg/controller/replication/replication_controller.go':[ +267, +427, +548, +], +'go-project:sources/go/kubernetes/pkg/controller/route/routecontroller.go':[ +115, +], +'go-project:sources/go/kubernetes/pkg/controller/service/servicecontroller.go':[ +257, +427, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/tokens_controller.go':[ +264, +358, +508, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_pod_control.go':[ +90, +], +'go-project:sources/go/kubernetes/pkg/controller/statefulset/stateful_set_control.go':[ +242, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/attach_detach_controller.go':[ +313, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go':[ +174, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/util/util.go':[ +181, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/index.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller.go':[ +248, +365, +432, +591, +1244, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller_base.go':[ +288, +346, +], +'go-project:sources/go/kubernetes/pkg/credentialprovider/keyring.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/authenticator/config.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/authorizer/config.go':[ +94, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/default_storage_factory_builder.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/options/cloudprovider.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/kubectl/apply.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/autoscale.go':[ +48, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/annotate.go':[ +180, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply.go':[ +184, +457, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go':[ +128, +188, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/attach.go':[ +204, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/autoscale.go':[ +82, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo.go':[ +61, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go':[ +91, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go':[ +159, +265, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/get_contexts.go':[ +104, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/set.go':[ +130, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create.go':[ +113, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_clusterrole.go':[ +103, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/create_role.go':[ +140, +238, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/delete.go':[ +247, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/describe.go':[ +103, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/drain.go':[ +486, +621, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/expose.go':[ +123, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/get.go':[ +148, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/help.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/label.go':[ +175, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/logs.go':[ +124, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/patch.go':[ +122, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/replace.go':[ +94, +173, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollingupdate.go':[ +142, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollout/rollout_status.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/run.go':[ +139, +517, +572, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/scale.go':[ +95, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/helper.go':[ +47, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_image.go':[ +172, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_resources.go':[ +179, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_selector.go':[ +157, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/set/set_subject.go':[ +168, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/taint.go':[ +176, +214, +321, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/testdata/edit/record.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +153, +370, +498, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/helpers.go':[ +130, +645, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/shortcut_restmapper.go':[ +111, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/version.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/kubectl/configmap.go':[ +165, +], +'go-project:sources/go/kubernetes/pkg/kubectl/env_file.go':[ +33, +], +'go-project:sources/go/kubernetes/pkg/kubectl/explain.go':[ +127, +178, +], +'go-project:sources/go/kubernetes/pkg/kubectl/history.go':[ +72, +], +'go-project:sources/go/kubernetes/pkg/kubectl/metricsutil/metrics_printer.go':[ +107, +], +'go-project:sources/go/kubernetes/pkg/kubectl/plugins/loader.go':[ +47, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/builder.go':[ +329, +621, +849, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/mapper.go':[ +124, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/result.go':[ +262, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/selector.go':[ +49, +], +'go-project:sources/go/kubernetes/pkg/kubectl/resource/visitor.go':[ +379, +527, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rollback.go':[ +214, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +169, +408, +696, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rollout_status.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/kubectl/scale.go':[ +193, +], +'go-project:sources/go/kubernetes/pkg/kubectl/secret.go':[ +165, +], +'go-project:sources/go/kubernetes/pkg/kubectl/service.go':[ +79, +], +'go-project:sources/go/kubernetes/pkg/kubectl/sorting_printer.go':[ +97, +175, +], +'go-project:sources/go/kubernetes/pkg/kubectl/stop.go':[ +399, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/cri/testing/fake_runtime_service.go':[ +311, +], +'go-project:sources/go/kubernetes/pkg/kubelet/certificate/certificate_manager.go':[ +354, +], +'go-project:sources/go/kubernetes/pkg/kubelet/certificate/certificate_store.go':[ +233, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/cgroup_manager_linux.go':[ +50, +440, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_linux.go':[ +132, +183, +350, +493, +764, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/helpers_linux.go':[ +87, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/node_container_manager.go':[ +57, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/pod_container_manager_linux.go':[ +180, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/config.go':[ +154, +213, +], +'go-project:sources/go/kubernetes/pkg/kubelet/configmap/configmap_manager.go':[ +188, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_container.go':[ +36, +291, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_legacy.go':[ +156, +220, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_sandbox.go':[ +76, +172, +414, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/exec.go':[ +66, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/fake_client.go':[ +382, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/helpers.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/eviction_manager.go':[ +214, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/helpers.go':[ +305, +670, +], +'go-project:sources/go/kubernetes/pkg/kubelet/gpu/nvidia/nvidia_gpu_manager.go':[ +213, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +295, +1208, +1447, +1869, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_network.go':[ +249, +355, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_node_status.go':[ +195, +417, +513, +674, +858, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_pods.go':[ +111, +370, +424, +1085, +1274, +1662, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet_volumes.go':[ +82, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_container.go':[ +620, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_gc.go':[ +214, +266, +327, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_logs.go':[ +121, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kuberuntime/kuberuntime_manager.go':[ +261, +436, +554, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/cni/cni.go':[ +95, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/fake_iptables.go':[ +154, +248, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/hostport/hostport_syncer.go':[ +178, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_linux.go':[ +313, +471, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pleg/generic.go':[ +181, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod_container_deletor.go':[ +63, +], +'go-project:sources/go/kubernetes/pkg/kubelet/prober/worker.go':[ +141, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +364, +632, +982, +1501, +1775, +1958, +2418, +], +'go-project:sources/go/kubernetes/pkg/kubelet/secret/secret_manager.go':[ +188, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/server.go':[ +450, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/stats/summary.go':[ +393, +], +'go-project:sources/go/kubernetes/pkg/kubelet/status/status_manager.go':[ +279, +371, +], +'go-project:sources/go/kubernetes/pkg/kubelet/sysctl/whitelist.go':[ +108, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util/csr/csr.go':[ +79, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/populator/desired_state_of_world_populator.go':[ +191, +], +'go-project:sources/go/kubernetes/pkg/kubelet/volumemanager/reconciler/reconciler.go':[ +165, +355, +496, +], +'go-project:sources/go/kubernetes/pkg/master/controller.go':[ +321, +], +'go-project:sources/go/kubernetes/pkg/master/master.go':[ +321, +], +'go-project:sources/go/kubernetes/pkg/master/thirdparty/tprregistration_controller.go':[ +73, +142, +253, +], +'go-project:sources/go/kubernetes/pkg/metrics/metrics_grabber.go':[ +141, +], +'go-project:sources/go/kubernetes/pkg/printers/customcolumn.go':[ +165, +203, +], +'go-project:sources/go/kubernetes/pkg/printers/humanreadable.go':[ +281, +456, +500, +689, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +219, +353, +432, +531, +609, +1235, +1815, +1931, +1996, +2055, +2103, +2359, +2585, +2786, +3302, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +207, +267, +585, +1087, +1702, +], +'go-project:sources/go/kubernetes/pkg/printers/jsonpath.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/printers/name.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/printers/printers.go':[ +32, +], +'go-project:sources/go/kubernetes/pkg/proxy/healthcheck/healthcheck.go':[ +126, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +525, +800, +845, +986, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +218, +398, +575, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxysocket.go':[ +116, +202, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxier.go':[ +318, +378, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +154, +361, +499, +594, +], +'go-project:sources/go/kubernetes/pkg/quota/generic/evaluator.go':[ +85, +], +'go-project:sources/go/kubernetes/pkg/registry/core/namespace/storage/storage.go':[ +116, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/storage/eviction.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/registry/core/pod/strategy.go':[ +274, +363, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/ipallocator/controller/repair.go':[ +90, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/portallocator/controller/repair.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +84, +341, +447, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/rest/thirdparty_controller.go':[ +78, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/codec.go':[ +334, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rest/storage_rbac.go':[ +134, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/policy_compact.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/policy_comparator.go':[ +58, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/validation/rule.go':[ +120, +], +'go-project:sources/go/kubernetes/pkg/security/apparmor/validate.go':[ +181, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/provider.go':[ +189, +], +'go-project:sources/go/kubernetes/pkg/serviceaccount/jwt.go':[ +197, +], +'go-project:sources/go/kubernetes/pkg/ssh/ssh.go':[ +431, +], +'go-project:sources/go/kubernetes/pkg/util/ipconfig/ipconfig.go':[ +58, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/save_restore.go':[ +64, +], +'go-project:sources/go/kubernetes/pkg/util/mount/mount_linux.go':[ +334, +], +'go-project:sources/go/kubernetes/pkg/util/oom/oom_linux.go':[ +89, +], +'go-project:sources/go/kubernetes/pkg/util/procfs/procfs_linux.go':[ +106, +], +'go-project:sources/go/kubernetes/pkg/util/removeall/removeall.go':[ +35, +], +'go-project:sources/go/kubernetes/pkg/util/version/version.go':[ +42, +155, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/attacher.go':[ +99, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/aws_ebs.go':[ +302, +], +'go-project:sources/go/kubernetes/pkg/volume/aws_ebs/aws_util.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/attacher.go':[ +213, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_common.go':[ +240, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_mounter.go':[ +69, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_provision.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_file/azure_file.go':[ +198, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder.go':[ +306, +], +'go-project:sources/go/kubernetes/pkg/volume/cinder/cinder_util.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/volume/configmap/configmap.go':[ +248, +], +'go-project:sources/go/kubernetes/pkg/volume/downwardapi/downwardapi.go':[ +221, +], +'go-project:sources/go/kubernetes/pkg/volume/flocker/flocker.go':[ +277, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/gce_pd.go':[ +265, +], +'go-project:sources/go/kubernetes/pkg/volume/gce_pd/gce_util.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/volume/glusterfs/glusterfs.go':[ +299, +910, +], +'go-project:sources/go/kubernetes/pkg/volume/iscsi/iscsi_util.go':[ +97, +193, +314, +], +'go-project:sources/go/kubernetes/pkg/volume/local/local.go':[ +190, +], +'go-project:sources/go/kubernetes/pkg/volume/nfs/nfs.go':[ +236, +], +'go-project:sources/go/kubernetes/pkg/volume/photon_pd/photon_pd.go':[ +203, +], +'go-project:sources/go/kubernetes/pkg/volume/portworx/portworx_util.go':[ +199, +], +'go-project:sources/go/kubernetes/pkg/volume/projected/projected.go':[ +223, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd.go':[ +257, +], +'go-project:sources/go/kubernetes/pkg/volume/rbd/rbd_util.go':[ +107, +244, +], +'go-project:sources/go/kubernetes/pkg/volume/scaleio/sio_volume.go':[ +87, +357, +], +'go-project:sources/go/kubernetes/pkg/volume/secret/secret.go':[ +247, +], +'go-project:sources/go/kubernetes/pkg/volume/storageos/storageos.go':[ +363, +], +'go-project:sources/go/kubernetes/pkg/volume/util.go':[ +63, +182, +], +'go-project:sources/go/kubernetes/pkg/volume/util/atomic_writer.go':[ +119, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_executor.go':[ +561, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go':[ +105, +181, +293, +358, +541, +622, +], +'go-project:sources/go/kubernetes/pkg/volume/volume_linux.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume.go':[ +209, +], +'go-project:sources/go/kubernetes/pkg/volume/vsphere_volume/vsphere_volume_util.go':[ +197, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/defaulttolerationseconds/admission.go':[ +65, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialization/initialization.go':[ +128, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/admission.go':[ +155, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/hawkular.go':[ +129, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/limitranger/admission.go':[ +89, +356, +431, +458, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/noderestriction/admission.go':[ +120, +204, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podnodeselector/admission.go':[ +194, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podpreset/admission.go':[ +90, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/admission.go':[ +75, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/controller.go':[ +209, +369, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/securitycontext/scdeny/admission.go':[ +47, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/serviceaccount/admission.go':[ +133, +309, +359, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/rbac.go':[ +70, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/rbac/subject_locator.go':[ +56, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +128, +227, +403, +502, +567, +828, +1060, +1115, +1177, +1317, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/interpod_affinity.go':[ +119, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go':[ +35, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/selector_spreading.go':[ +63, +102, +227, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/extender.go':[ +100, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/core/generic_scheduler.go':[ +163, +285, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +651, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/schedulercache/node_info.go':[ +293, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/schedulercache/reconcile_affinity.go':[ +35, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/examples/client-go/client/cr.go':[ +38, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go':[ +105, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/controller/status/naming_controller.go':[ +123, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/registry/customresourcedefinition/etcd.go':[ +70, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/help.go':[ +69, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/multirestmapper.go':[ +53, +88, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/meta/restmapper.go':[ +211, +310, +485, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/amount.go':[ +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/math.go':[ +173, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity.go':[ +158, +275, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go':[ +92, +181, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go':[ +255, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/valuefuzz.go':[ +29, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go':[ +147, +259, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/cloner.go':[ +171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +582, +788, +845, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/queryparams/convert.go':[ +155, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/unstructured/converter.go':[ +137, +300, +356, +419, +510, +631, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +114, +173, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/scheme.go':[ +483, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/json/json.go':[ +74, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/protobuf/protobuf.go':[ +88, +319, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/recognizer/recognizer.go':[ +84, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/streaming/streaming.go':[ +74, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/serializer/versioning/versioning.go':[ +96, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/diff/diff.go':[ +132, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/mergepatch/util.go':[ +92, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/http.go':[ +258, +310, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/interface.go':[ +102, +160, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +154, +415, +536, +611, +693, +924, +995, +1058, +1228, +1360, +1415, +1555, +1747, +1788, +1837, +1958, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/yaml/decoder.go':[ +269, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/watch/until.go':[ +40, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/json/fields.go':[ +142, +396, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/reflect/deep_equal.go':[ +103, +246, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/reflect/type.go':[ +43, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/config.go':[ +73, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go':[ +88, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/policy/checker.go':[ +61, +130, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/request.go':[ +109, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/audit.go':[ +40, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/impersonation.go':[ +38, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/legacy_audit.go':[ +99, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/negotiation/negotiate.go':[ +115, +204, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/proxy.go':[ +55, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/response.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +150, +289, +401, +530, +613, +847, +942, +1062, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/watch.go':[ +242, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +191, +891, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/request/requestinfo.go':[ +105, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +277, +329, +463, +786, +956, +1171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/rest/proxy.go':[ +138, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/delete.go':[ +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go':[ +1270, +1319, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/config.go':[ +296, +377, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/cors.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/maxinflight.go':[ +45, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi.go':[ +125, +173, +250, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi_aggregator.go':[ +68, +216, +276, +323, +427, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig/config.go':[ +104, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/options/serving.go':[ +182, +244, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +457, +849, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_helper.go':[ +175, +372, +477, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/testing/utils.go':[ +117, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go':[ +255, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/dial.go':[ +32, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/proxy/transport.go':[ +166, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/wsstream/stream.go':[ +142, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go':[ +211, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/discovery_client.go':[ +245, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/restmapper.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/clientset.go':[ +419, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/helper/qos/qos.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +35, +131, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/defaults.go':[ +126, +300, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/ref/ref.go':[ +40, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/resource/helpers.go':[ +30, +77, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go':[ +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/defaults.go':[ +71, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/conversion.go':[ +45, +116, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/rbac/helpers.go':[ +206, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/plugin/pkg/client/auth/azure/azure.go':[ +171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/request.go':[ +442, +774, +921, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/fixture.go':[ +68, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/third_party/forked/golang/template/funcs.go':[ +111, +172, +303, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/controller.go':[ +276, +342, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/delta_fifo.go':[ +466, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/listers.go':[ +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/listwatch.go':[ +99, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/mutation_cache.go':[ +118, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/reflector.go':[ +235, +340, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/shared_informer.go':[ +336, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/v1/conversion.go':[ +27, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go':[ +108, +210, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/config.go':[ +156, +354, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/loader.go':[ +167, +239, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/validation.go':[ +198, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/v1.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/remotecommand/v4.go':[ +82, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/transport/round_trippers.go':[ +379, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/homedir/homedir.go':[ +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/jsonpath.go':[ +77, +234, +295, +329, +370, +397, +433, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/parser.go':[ +251, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/workqueue/delaying_queue.go':[ +171, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go':[ +30, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/controllers/status/available_controller.go':[ +107, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/cluster_size_autoscaling.go':[ +768, +943, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/es_utils.go':[ +50, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/sd_utils.go':[ +173, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/utils.go':[ +141, +179, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_set.go':[ +622, +718, +], +'go-project:sources/go/kubernetes/test/e2e/deployment.go':[ +1207, +], +'go-project:sources/go/kubernetes/test/e2e/dns.go':[ +172, +], +'go-project:sources/go/kubernetes/test/e2e/e2e.go':[ +66, +], +'go-project:sources/go/kubernetes/test/e2e/extension/initializers.go':[ +246, +], +'go-project:sources/go/kubernetes/test/e2e/framework/framework.go':[ +239, +], +'go-project:sources/go/kubernetes/test/e2e/framework/google_compute.go':[ +104, +], +'go-project:sources/go/kubernetes/test/e2e/framework/ingress_utils.go':[ +415, +538, +], +'go-project:sources/go/kubernetes/test/e2e/framework/kubelet_stats.go':[ +345, +852, +], +'go-project:sources/go/kubernetes/test/e2e/framework/metrics_util.go':[ +404, +], +'go-project:sources/go/kubernetes/test/e2e/framework/networking_utils.go':[ +174, +], +'go-project:sources/go/kubernetes/test/e2e/framework/pv_util.go':[ +217, +439, +680, +739, +], +'go-project:sources/go/kubernetes/test/e2e/framework/resource_usage_gatherer.go':[ +219, +278, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +374, +946, +1025, +1211, +], +'go-project:sources/go/kubernetes/test/e2e/framework/statefulset_utils.go':[ +451, +691, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +525, +571, +720, +992, +1114, +1221, +1642, +2017, +2545, +3213, +3970, +4137, +4587, +5415, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet.go':[ +101, +], +'go-project:sources/go/kubernetes/test/e2e/kubelet_perf.go':[ +123, +159, +], +'go-project:sources/go/kubernetes/test/e2e/monitoring.go':[ +123, +], +'go-project:sources/go/kubernetes/test/e2e/portforward.go':[ +376, +], +'go-project:sources/go/kubernetes/test/e2e/pre_stop.go':[ +39, +], +'go-project:sources/go/kubernetes/test/e2e/reboot.go':[ +172, +], +'go-project:sources/go/kubernetes/test/e2e/scheduling/predicates.go':[ +897, +], +'go-project:sources/go/kubernetes/test/e2e/service_latency.go':[ +212, +], +'go-project:sources/go/kubernetes/test/e2e/stackdriver_monitoring.go':[ +103, +], +'go-project:sources/go/kubernetes/test/e2e/storage/pd.go':[ +706, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_diskformat.go':[ +163, +], +'go-project:sources/go/kubernetes/test/e2e_federation/deployment.go':[ +143, +188, +], +'go-project:sources/go/kubernetes/test/e2e_federation/namespace.go':[ +191, +], +'go-project:sources/go/kubernetes/test/e2e_federation/replicaset.go':[ +391, +], +'go-project:sources/go/kubernetes/test/e2e_federation/service.go':[ +321, +], +'go-project:sources/go/kubernetes/test/e2e_node/runner/remote/run_remote.go':[ +161, +505, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/server.go':[ +146, +], +'go-project:sources/go/kubernetes/test/e2e_node/services/services.go':[ +151, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/kernel_validator.go':[ +101, +172, +], +'go-project:sources/go/kubernetes/test/e2e_node/system/package_validator.go':[ +276, +], +'go-project:sources/go/kubernetes/test/images/netexec/netexec.go':[ +142, +289, +], +'go-project:sources/go/kubernetes/test/images/port-forward-tester/portforwardtester.go':[ +61, +], +'go-project:sources/go/kubernetes/test/images/serve_hostname/serve_hostname.go':[ +38, +], +'go-project:sources/go/kubernetes/test/integration/framework/master_utils.go':[ +181, +], +'go-project:sources/go/kubernetes/test/list/main.go':[ +185, +], +'go-project:sources/go/kubernetes/test/soak/cauldron/cauldron.go':[ +59, +], +'go-project:sources/go/kubernetes/test/soak/serve_hostnames/serve_hostnames.go':[ +64, +], +'go-project:sources/go/kubernetes/test/utils/density_utils.go':[ +60, +], +'go-project:sources/go/kubernetes/test/utils/deployment.go':[ +74, +145, +], +'go-project:sources/go/kubernetes/test/utils/runners.go':[ +203, +558, +633, +678, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/pkg/fileutil/purge.go':[ +25, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/repair.go':[ +29, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/wal.go':[ +146, +230, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/pkg/fileutil/purge.go':[ +25, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/repair.go':[ +29, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/wal.go':[ +144, +228, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/reflect/deep_equal.go':[ +103, +246, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/reflect/type.go':[ +43, +], +'go-project:sources/go/kubernetes/third_party/forked/golang/template/funcs.go':[ +111, +172, +303, +], +'go-project:sources/go/kubernetes/third_party/forked/gonum/graph/traverse/traverse.go':[ +28, +120, +], +'go-project:sources/go/kubernetes/third_party/forked/gonum/graph/traverse/visit_depth_first.go':[ +37, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S3923.json b/its/ruling/src/test/resources/expected/go/go-S3923.json new file mode 100644 index 00000000..4162196d --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S3923.json @@ -0,0 +1,5 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S3923.go':[ +4, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S4144.json b/its/ruling/src/test/resources/expected/go/go-S4144.json new file mode 100644 index 00000000..192e5b3b --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S4144.json @@ -0,0 +1,80 @@ +{ +'go-project:sources/go/kubernetes/federation/client/clientset_generated/federation_clientset/clientset.go':[ +71, +88, +105, +122, +139, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/clientset.go':[ +134, +151, +168, +185, +210, +235, +260, +285, +302, +319, +336, +353, +378, +403, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws_loadbalancer.go':[ +370, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/rackspace/rackspace.go':[ +427, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/testing/cadvisor_mock.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/os.go':[ +76, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/testing/runtime_mock.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/fake_client.go':[ +481, +], +'go-project:sources/go/kubernetes/pkg/registry/authorization/rest/storage_authorization.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_common.go':[ +372, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/clientset.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/clientset.go':[ +134, +151, +168, +185, +210, +235, +260, +285, +302, +319, +336, +353, +378, +403, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/thread_safe_store.go':[ +76, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/flowcontrol/backoff.go':[ +133, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/clientset.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/metrics/pkg/client/clientset_generated/clientset/clientset.go':[ +51, +], +} diff --git a/its/ruling/src/test/resources/expected/go/go-S4663.json b/its/ruling/src/test/resources/expected/go/go-S4663.json new file mode 100644 index 00000000..01cf85f8 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/go-S4663.json @@ -0,0 +1,5 @@ +{ +'go-project:ruling/src/test/resources/sources/go/S4663.go':[ +7, +], +} diff --git a/its/ruling/src/test/resources/expected/go/old/go-S1066.json b/its/ruling/src/test/resources/expected/go/old/go-S1066.json new file mode 100644 index 00000000..dd5ccad7 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/old/go-S1066.json @@ -0,0 +1,408 @@ +{ +'go-project:sources/go/kubernetes/cmd/kubeadm/app/phases/apiconfig/clusterroles.go':[ +69, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +392, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/generator.go':[ +311, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +218, +242, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +212, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/deployment/deploymentcontroller.go':[ +587, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +922, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/namespace/namespace_controller.go':[ +451, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/replicaset/replicasetcontroller.go':[ +565, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/dns/dns.go':[ +302, +307, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +322, +508, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/sync/controller.go':[ +415, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/federated_informer.go':[ +236, +], +'go-project:sources/go/kubernetes/pkg/api/pod/util.go':[ +67, +100, +107, +137, +154, +161, +], +'go-project:sources/go/kubernetes/pkg/api/v1/pod/util.go':[ +155, +188, +195, +225, +242, +249, +], +'go-project:sources/go/kubernetes/pkg/api/v1/validation/validation.go':[ +70, +82, +110, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +116, +1156, +1218, +2082, +3000, +3110, +3235, +3415, +3429, +3441, +3459, +3868, +3945, +4145, +4151, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/conversion.go':[ +244, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/validation/validation.go':[ +68, +], +'go-project:sources/go/kubernetes/pkg/auth/authorizer/abac/abac.go':[ +117, +211, +212, +213, +], +'go-project:sources/go/kubernetes/pkg/client/unversioned/testclient/simple/simple_testclient.go':[ +158, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/aws/aws.go':[ +1507, +1553, +1931, +3185, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/gce/gce_instances.go':[ +139, +160, +179, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/photon/photon.go':[ +327, +], +'go-project:sources/go/kubernetes/pkg/cloudprovider/providers/vsphere/vsphere.go':[ +486, +927, +1904, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +237, +296, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/cronjob_controller.go':[ +390, +], +'go-project:sources/go/kubernetes/pkg/controller/namespace/deletion/namespaced_resources_deleter.go':[ +319, +], +'go-project:sources/go/kubernetes/pkg/controller/node/controller_utils.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/controller/node/nodecontroller.go':[ +610, +637, +], +'go-project:sources/go/kubernetes/pkg/controller/route/routecontroller.go':[ +175, +], +'go-project:sources/go/kubernetes/pkg/controller/serviceaccount/tokens_controller.go':[ +622, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/reconciler/reconciler.go':[ +140, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/index.go':[ +146, +], +'go-project:sources/go/kubernetes/pkg/kubeapiserver/authenticator/config.go':[ +140, +196, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/auth/cani.go':[ +100, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/view.go':[ +133, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/get.go':[ +517, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +92, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editor.go':[ +129, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/sanity/cmd_sanity.go':[ +86, +], +'go-project:sources/go/kubernetes/pkg/kubectl/stop.go':[ +383, +], +'go-project:sources/go/kubernetes/pkg/kubelet/apis/cri/testing/fake_image_service.go':[ +79, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/helpers.go':[ +87, +183, +189, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_service.go':[ +397, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/libdocker/helpers.go':[ +74, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/eviction_manager.go':[ +515, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/helpers.go':[ +704, +892, +], +'go-project:sources/go/kubernetes/pkg/kubelet/kubelet.go':[ +542, +1580, +1596, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_linux.go':[ +203, +], +'go-project:sources/go/kubernetes/pkg/kubelet/remote/remote_image.go':[ +87, +], +'go-project:sources/go/kubernetes/pkg/printers/humanreadable.go':[ +758, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +541, +554, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxier.go':[ +82, +224, +230, +238, +244, +251, +272, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/proxysocket.go':[ +166, +214, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxier.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxysocket.go':[ +204, +394, +524, +], +'go-project:sources/go/kubernetes/pkg/quota/resources.go':[ +78, +92, +], +'go-project:sources/go/kubernetes/pkg/registry/authentication/rest/storage_authentication.go':[ +62, +76, +], +'go-project:sources/go/kubernetes/pkg/registry/core/service/rest.go':[ +94, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/reconciliation/reconcile_rolebindings.go':[ +236, +243, +], +'go-project:sources/go/kubernetes/pkg/util/exec/exec.go':[ +152, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/iptables.go':[ +222, +468, +], +'go-project:sources/go/kubernetes/pkg/util/netsh/netsh.go':[ +77, +96, +140, +158, +], +'go-project:sources/go/kubernetes/pkg/util/version/version.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/volume/azure_dd/azure_provision.go':[ +83, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/attacher.go':[ +110, +], +'go-project:sources/go/kubernetes/pkg/volume/scaleio/sio_mgr.go':[ +135, +], +'go-project:sources/go/kubernetes/pkg/volume/storageos/storageos_util.go':[ +350, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podnodeselector/admission.go':[ +83, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podtolerationrestriction/admission.go':[ +156, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/controller.go':[ +445, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/serviceaccount/admission.go':[ +328, +338, +], +'go-project:sources/go/kubernetes/plugin/pkg/auth/authorizer/node/graph_populator.go':[ +60, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +143, +847, +848, +1035, +1048, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_prefer_avoid_pods.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/labels.go':[ +94, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/labels/selector.go':[ +636, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/http.go':[ +166, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/net/interface.go':[ +175, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go':[ +1592, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/audit/validation/validation.go':[ +37, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/audit/policy/checker.go':[ +62, +79, +135, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +496, +1195, +1261, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +635, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/config.go':[ +272, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/storage/resource_config.go':[ +177, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/cacher.go':[ +555, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/storage/etcd/etcd_helper.go':[ +189, +219, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/conversion.go':[ +244, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/fixture.go':[ +222, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/cache/mutation_cache.go':[ +212, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/api/helpers.go':[ +161, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/loader.go':[ +567, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/validation.go':[ +268, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/jsonpath/jsonpath.go':[ +313, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/util/testing/fake_handler.go':[ +134, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +387, +974, +984, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +2576, +4161, +4395, +4542, +4575, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +581, +], +'go-project:sources/go/kubernetes/test/e2e/service_latency.go':[ +294, +301, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_diskformat.go':[ +208, +], +'go-project:sources/go/kubernetes/test/e2e_node/runner/remote/run_remote.go':[ +249, +], +'go-project:sources/go/kubernetes/test/soak/cauldron/cauldron.go':[ +110, +], +'go-project:sources/go/kubernetes/test/soak/serve_hostnames/serve_hostnames.go':[ +128, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/wal/wal.go':[ +174, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/wal/wal.go':[ +172, +], +'go-project:sources/go/kubernetes/third_party/forked/gonum/graph/simple/directed_acyclic.go':[ +48, +77, +], +} diff --git a/its/ruling/src/test/resources/expected/go/old/go-S1116.json b/its/ruling/src/test/resources/expected/go/old/go-S1116.json new file mode 100644 index 00000000..df35d05f --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/old/go-S1116.json @@ -0,0 +1,6 @@ +{ +'project:samples/EmptyStatements.go':[ +9, +11, +], +} diff --git a/its/ruling/src/test/resources/expected/go/old/go-S1172.json b/its/ruling/src/test/resources/expected/go/old/go-S1172.json new file mode 100644 index 00000000..df641458 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/old/go-S1172.json @@ -0,0 +1,1092 @@ +{ +'go-project:sources/go/kubernetes/cmd/cloud-controller-manager/app/controllermanager.go':[ +69, +197, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/federation-apiserver.go':[ +33, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/federation-controller-manager.go':[ +32, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/kube-apiserver.go':[ +35, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/kube-controller-manager.go':[ +34, +], +'go-project:sources/go/kubernetes/cmd/hyperkube/kubectl.go':[ +36, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/aggregator.go':[ +175, +179, +], +'go-project:sources/go/kubernetes/cmd/kube-apiserver/app/server.go':[ +102, +], +'go-project:sources/go/kubernetes/cmd/kube-controller-manager/app/controllermanager.go':[ +88, +], +'go-project:sources/go/kubernetes/cmd/kube-proxy/app/server.go':[ +339, +647, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/fuzzer/fuzzer.go':[ +26, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/apis/kubeadm/validation/validation.go':[ +215, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/cmd.go':[ +30, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/init.go':[ +80, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/join.go':[ +137, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/phases/certs.go':[ +56, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/phases/kubeconfig.go':[ +49, +66, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/phases/validate.go':[ +33, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/reset.go':[ +43, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/token.go':[ +78, +138, +157, +188, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/cmd/version.go':[ +43, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/master/manifests.go':[ +190, +], +'go-project:sources/go/kubernetes/cmd/kubeadm/app/util/error.go':[ +62, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server.go':[ +126, +347, +651, +], +'go-project:sources/go/kubernetes/cmd/kubelet/app/server_unsupported.go':[ +23, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/client_generator.go':[ +126, +143, +188, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/client-gen/generators/fake/fake_client_generator.go':[ +80, +107, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/conversion-gen/generators/conversion.go':[ +83, +299, +304, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/import_tracker.go':[ +32, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/go-to-protobuf/protobuf/parser.go':[ +357, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/informer-gen/generators/packages.go':[ +226, +253, +260, +282, +294, +308, +335, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/lister-gen/generators/lister.go':[ +132, +156, +], +'go-project:sources/go/kubernetes/cmd/libs/go2idl/openapi-gen/generators/openapi.go':[ +133, +136, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/headers.go':[ +59, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/kubectl_dash_f.go':[ +28, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/mungedocs.go':[ +152, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/preformatted.go':[ +23, +46, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/toc.go':[ +36, +], +'go-project:sources/go/kubernetes/cmd/mungedocs/whitespace.go':[ +20, +], +'go-project:sources/go/kubernetes/examples/explorer/explorer.go':[ +54, +62, +67, +70, +], +'go-project:sources/go/kubernetes/examples/guestbook-go/main.go':[ +51, +56, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-apiserver/app/server.go':[ +69, +], +'go-project:sources/go/kubernetes/federation/cmd/federation-controller-manager/app/controllermanager.go':[ +71, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/route53.go':[ +54, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/rrsets.go':[ +39, +61, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/providers/aws/route53/zones.go':[ +37, +], +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/tests/commontests.go':[ +164, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/deployment/deploymentcontroller.go':[ +112, +130, +133, +139, +152, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/ingress/ingress_controller.go':[ +166, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/namespace/namespace_controller.go':[ +133, +152, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/replicaset/replicasetcontroller.go':[ +114, +132, +135, +141, +154, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/service/servicecontroller.go':[ +140, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/sync/controller.go':[ +146, +168, +], +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/test/test_helper.go':[ +168, +179, +295, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/init/init.go':[ +557, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/join.go':[ +537, +], +'go-project:sources/go/kubernetes/federation/pkg/kubefed/kubefed.go':[ +34, +75, +], +'go-project:sources/go/kubernetes/pkg/api/testing/fuzzer.go':[ +111, +117, +121, +429, +465, +468, +503, +583, +620, +683, +717, +736, +745, +754, +], +'go-project:sources/go/kubernetes/pkg/api/v1/conversion.go':[ +285, +327, +709, +], +'go-project:sources/go/kubernetes/pkg/api/validation/validation.go':[ +214, +870, +2956, +], +'go-project:sources/go/kubernetes/pkg/apis/abac/v0/conversion.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/apis/admissionregistration/validation/validation.go':[ +174, +244, +], +'go-project:sources/go/kubernetes/pkg/apis/apps/v1beta1/conversion.go':[ +168, +179, +191, +210, +], +'go-project:sources/go/kubernetes/pkg/apis/autoscaling/v1/conversion.go':[ +236, +255, +], +'go-project:sources/go/kubernetes/pkg/apis/batch/validation/validation.go':[ +155, +], +'go-project:sources/go/kubernetes/pkg/apis/certificates/validation/validation.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/apis/extensions/v1beta1/conversion.go':[ +85, +104, +390, +401, +], +'go-project:sources/go/kubernetes/pkg/apis/networking/v1/conversion.go':[ +157, +168, +], +'go-project:sources/go/kubernetes/pkg/apis/rbac/validation/validation.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/clientset/typed/core/v1/event_expansion.go':[ +145, +], +'go-project:sources/go/kubernetes/pkg/client/clientset_generated/internalclientset/typed/core/internalversion/event_expansion.go':[ +146, +], +'go-project:sources/go/kubernetes/pkg/controller/bootstrap/tokencleaner.go':[ +94, +], +'go-project:sources/go/kubernetes/pkg/controller/cloud/nodecontroller.go':[ +252, +], +'go-project:sources/go/kubernetes/pkg/controller/cronjob/cronjob_controller.go':[ +364, +], +'go-project:sources/go/kubernetes/pkg/controller/endpoint/endpoints_controller.go':[ +85, +], +'go-project:sources/go/kubernetes/pkg/controller/job/jobcontroller.go':[ +105, +], +'go-project:sources/go/kubernetes/pkg/controller/namespace/namespace_controller.go':[ +89, +], +'go-project:sources/go/kubernetes/pkg/controller/node/controller_utils.go':[ +335, +], +'go-project:sources/go/kubernetes/pkg/controller/node/timed_workers.go':[ +84, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/attachdetach/testing/testvolumespec.go':[ +67, +127, +], +'go-project:sources/go/kubernetes/pkg/controller/volume/persistentvolume/pv_controller_base.go':[ +100, +111, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apiversions.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/apply_set_last_applied.go':[ +91, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/auth/cani.go':[ +95, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/certificates.go':[ +38, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo.go':[ +51, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/clusterinfo_dump.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/cmd.go':[ +387, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_authinfo.go':[ +107, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_cluster.go':[ +70, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/create_context.go':[ +61, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/current_context.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/delete_cluster.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/delete_context.go':[ +42, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/get_clusters.go':[ +44, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/set.go':[ +62, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/unset.go':[ +50, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/use_context.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/config/view.go':[ +70, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/convert.go':[ +70, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/logs.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/options.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/proxy.go':[ +78, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/rollout/rollout_status.go':[ +150, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/templates/templater.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/util/editor/editoptions.go':[ +362, +371, +503, +593, +605, +], +'go-project:sources/go/kubernetes/pkg/kubectl/cmd/version.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/kubectl/rolling_updater.go':[ +696, +], +'go-project:sources/go/kubernetes/pkg/kubectl/stop.go':[ +224, +], +'go-project:sources/go/kubernetes/pkg/kubectl/util/util.go':[ +29, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/cadvisor_unsupported.go':[ +34, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cadvisor/cadvisor_windows.go':[ +33, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/cgroup_manager_unsupported.go':[ +73, +77, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_unsupported.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/container_manager_windows.go':[ +41, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/helpers_unsupported.go':[ +33, +38, +43, +52, +], +'go-project:sources/go/kubernetes/pkg/kubelet/cm/util/cgroups_unsupported.go':[ +21, +], +'go-project:sources/go/kubernetes/pkg/kubelet/config/file.go':[ +52, +], +'go-project:sources/go/kubernetes/pkg/kubelet/container/pty_unsupported.go':[ +26, +], +'go-project:sources/go/kubernetes/pkg/kubelet/dockershim/docker_image.go':[ +141, +], +'go-project:sources/go/kubernetes/pkg/kubelet/eviction/threshold_notifier_unsupported.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/kubelet/network/kubenet/kubenet_unsupported.go':[ +33, +], +'go-project:sources/go/kubernetes/pkg/kubelet/pod_workers.go':[ +320, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/log.go':[ +40, +], +'go-project:sources/go/kubernetes/pkg/kubelet/rkt/rkt.go':[ +233, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/httpstream.go':[ +75, +], +'go-project:sources/go/kubernetes/pkg/kubelet/server/portforward/websocket.go':[ +96, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util/util_linux.go':[ +54, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util/util_unsupported.go':[ +27, +31, +], +'go-project:sources/go/kubernetes/pkg/kubelet/util/util_windows.go':[ +43, +], +'go-project:sources/go/kubernetes/pkg/master/tunneler/ssh.go':[ +53, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/describe.go':[ +1275, +], +'go-project:sources/go/kubernetes/pkg/printers/internalversion/printers.go':[ +1963, +], +'go-project:sources/go/kubernetes/pkg/proxy/iptables/proxier.go':[ +769, +], +'go-project:sources/go/kubernetes/pkg/proxy/userspace/rlimit_windows.go':[ +21, +], +'go-project:sources/go/kubernetes/pkg/proxy/winuserspace/proxier.go':[ +141, +155, +], +'go-project:sources/go/kubernetes/pkg/quota/generic/evaluator.go':[ +61, +], +'go-project:sources/go/kubernetes/pkg/registry/autoscaling/horizontalpodautoscaler/strategy.go':[ +89, +], +'go-project:sources/go/kubernetes/pkg/registry/core/limitrange/strategy.go':[ +79, +], +'go-project:sources/go/kubernetes/pkg/registry/core/node/strategy.go':[ +176, +], +'go-project:sources/go/kubernetes/pkg/registry/core/podtemplate/strategy.go':[ +87, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresource/strategy.go':[ +100, +], +'go-project:sources/go/kubernetes/pkg/registry/extensions/thirdpartyresourcedata/strategy.go':[ +98, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrole/policybased/storage.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrole/strategy.go':[ +125, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrolebinding/policybased/storage.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/clusterrolebinding/strategy.go':[ +125, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/role/policybased/storage.go':[ +60, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/role/strategy.go':[ +125, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rolebinding/policybased/storage.go':[ +77, +], +'go-project:sources/go/kubernetes/pkg/registry/rbac/rolebinding/strategy.go':[ +125, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/selinux/runasany.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/user/nonroot.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/security/podsecuritypolicy/user/runasany.go':[ +31, +], +'go-project:sources/go/kubernetes/pkg/util/async/runner.go':[ +33, +], +'go-project:sources/go/kubernetes/pkg/util/bandwidth/unsupported.go':[ +30, +], +'go-project:sources/go/kubernetes/pkg/util/configz/configz.go':[ +71, +], +'go-project:sources/go/kubernetes/pkg/util/flock/flock_other.go':[ +22, +], +'go-project:sources/go/kubernetes/pkg/util/iptables/iptables_unsupported.go':[ +26, +30, +], +'go-project:sources/go/kubernetes/pkg/util/oom/oom_fake.go':[ +23, +29, +33, +], +'go-project:sources/go/kubernetes/pkg/util/oom/oom_unsupported.go':[ +34, +38, +], +'go-project:sources/go/kubernetes/pkg/util/procfs/procfs_unsupported.go':[ +39, +45, +], +'go-project:sources/go/kubernetes/pkg/util/resourcecontainer/resource_container_unsupported.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/util/rlimit/rlimit_unsupported.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/util/term/setsize_unsupported.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/util/umask_windows.go':[ +25, +], +'go-project:sources/go/kubernetes/pkg/volume/flexvolume/driver-call.go':[ +166, +], +'go-project:sources/go/kubernetes/pkg/volume/util/atomic_writer.go':[ +315, +], +'go-project:sources/go/kubernetes/pkg/volume/util/fs_unsupported.go':[ +28, +32, +36, +], +'go-project:sources/go/kubernetes/pkg/volume/util/operationexecutor/operation_generator.go':[ +731, +], +'go-project:sources/go/kubernetes/pkg/volume/volume_unsupported.go':[ +21, +], +'go-project:sources/go/kubernetes/plugin/cmd/kube-scheduler/app/server.go':[ +59, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/admit/admission.go':[ +27, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/alwayspullimages/admission.go':[ +37, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/antiaffinity/admission.go':[ +31, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/defaulttolerationseconds/admission.go':[ +43, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/deny/admission.go':[ +28, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/exec/admission.go':[ +34, +40, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/gc/gc_admission.go':[ +35, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialization/initialization.go':[ +43, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/admission.go':[ +50, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/initialresources/influxdb.go':[ +39, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/limitranger/admission.go':[ +48, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/namespace/autoprovision/admission.go':[ +35, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/namespace/exists/admission.go':[ +35, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/noderestriction/admission.go':[ +41, +144, +149, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/persistentvolume/label/admission.go':[ +37, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/podpreset/admission.go':[ +46, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/resourcequota/apis/resourcequota/v1alpha1/defaults.go':[ +25, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/security/podsecuritypolicy/admission.go':[ +49, +177, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/securitycontext/scdeny/admission.go':[ +30, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/serviceaccount/admission.go':[ +58, +154, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/storageclass/setdefault/admission.go':[ +43, +], +'go-project:sources/go/kubernetes/plugin/pkg/admission/webhook/admission.go':[ +69, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/predicates/predicates.go':[ +184, +694, +705, +1235, +1243, +1249, +1290, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/image_locality.go':[ +32, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/metadata.go':[ +32, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_affinity.go':[ +78, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/node_prefer_avoid_pods.go':[ +29, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/priorities/taint_toleration.go':[ +77, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithm/types.go':[ +61, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/algorithmprovider/defaults/defaults.go':[ +59, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/factory.go':[ +176, +], +'go-project:sources/go/kubernetes/plugin/pkg/scheduler/factory/plugins.go':[ +136, +235, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiextensions-apiserver/pkg/cmd/server/start.go':[ +60, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/fuzzer.go':[ +50, +56, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go':[ +218, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/conversion.go':[ +55, +66, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion/register.go':[ +51, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go':[ +74, +83, +89, +98, +104, +113, +119, +128, +138, +147, +158, +166, +172, +178, +185, +193, +202, +211, +219, +228, +233, +244, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go':[ +776, +781, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/watch.go':[ +40, +57, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/cloner.go':[ +45, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/conversion/converter.go':[ +125, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/conversion.go':[ +49, +57, +73, +87, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/runtime/embedded.go':[ +91, +112, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go':[ +41, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/util/runtime/runtime.go':[ +69, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/pkg/watch/watch.go':[ +97, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apimachinery/third_party/forked/golang/json/fields.go':[ +368, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle/admission.go':[ +55, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/admission/plugins.go':[ +45, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/fuzzer/fuzzer.go':[ +67, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/v1/conversion.go':[ +23, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/apis/example/v1/defaults.go':[ +23, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/request/anonymous/anonymous.go':[ +33, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/authentication/serviceaccount/util.go':[ +63, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authentication.go':[ +87, +93, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/patch.go':[ +110, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/errors.go':[ +40, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/responsewriters/writers.go':[ +89, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/rest.go':[ +830, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/endpoints/installer.go':[ +863, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/storage_factory.go':[ +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/registry/store.go':[ +211, +680, +1230, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/generic/storage_decorator.go':[ +44, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/registry/rest/resttest/resttest.go':[ +630, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/longrunning.go':[ +28, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/filters/timeout.go':[ +70, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/healthz/healthz.go':[ +57, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/httplog/log.go':[ +114, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/server/openapi/openapi_aggregator.go':[ +46, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/flag/flags.go':[ +28, +36, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/apiserver/pkg/util/wsstream/conn.go':[ +108, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/discovery/discovery_client.go':[ +316, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/examples/workqueue/main.go':[ +181, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/kubernetes/typed/core/v1/event_expansion.go':[ +145, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/api/v1/conversion.go':[ +285, +327, +709, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/apps/v1beta1/conversion.go':[ +168, +179, +191, +210, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/autoscaling/v1/conversion.go':[ +236, +255, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/extensions/v1beta1/conversion.go':[ +85, +104, +390, +401, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/apis/networking/v1/conversion.go':[ +157, +168, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/pkg/util/umask_windows.go':[ +25, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/plugin/pkg/client/auth/azure/azure.go':[ +277, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/rest/client.go':[ +192, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/testing/fixture.go':[ +393, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/clientcmd/client_config.go':[ +190, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/tools/record/events_cache.go':[ +66, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/apis/apiregistration/validation/validation.go':[ +34, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/kube-aggregator/pkg/cmd/server/start.go':[ +65, +], +'go-project:sources/go/kubernetes/staging/src/k8s.io/sample-apiserver/pkg/cmd/server/start.go':[ +59, +], +'go-project:sources/go/kubernetes/test/e2e/autoscaling/cluster_size_autoscaling.go':[ +613, +], +'go-project:sources/go/kubernetes/test/e2e/cluster-logging/utils.go':[ +141, +], +'go-project:sources/go/kubernetes/test/e2e/common/downward_api.go':[ +220, +], +'go-project:sources/go/kubernetes/test/e2e/common/empty_dir.go':[ +130, +189, +240, +], +'go-project:sources/go/kubernetes/test/e2e/common/events.go':[ +65, +], +'go-project:sources/go/kubernetes/test/e2e/daemon_restart.go':[ +232, +], +'go-project:sources/go/kubernetes/test/e2e/dns.go':[ +45, +], +'go-project:sources/go/kubernetes/test/e2e/e2e.go':[ +229, +], +'go-project:sources/go/kubernetes/test/e2e/examples.go':[ +377, +], +'go-project:sources/go/kubernetes/test/e2e/framework/framework.go':[ +661, +], +'go-project:sources/go/kubernetes/test/e2e/framework/kubelet_stats.go':[ +262, +], +'go-project:sources/go/kubernetes/test/e2e/framework/networking_utils.go':[ +836, +], +'go-project:sources/go/kubernetes/test/e2e/framework/service_util.go':[ +1287, +1335, +1336, +], +'go-project:sources/go/kubernetes/test/e2e/framework/statefulset_utils.go':[ +360, +], +'go-project:sources/go/kubernetes/test/e2e/framework/util.go':[ +663, +680, +694, +706, +2420, +5292, +], +'go-project:sources/go/kubernetes/test/e2e/garbage_collector.go':[ +70, +116, +], +'go-project:sources/go/kubernetes/test/e2e/kubectl.go':[ +245, +309, +1738, +1952, +], +'go-project:sources/go/kubernetes/test/e2e/monitoring.go':[ +232, +], +'go-project:sources/go/kubernetes/test/e2e/network_partition.go':[ +178, +549, +], +'go-project:sources/go/kubernetes/test/e2e/networking_perf.go':[ +89, +], +'go-project:sources/go/kubernetes/test/e2e/perf/density.go':[ +625, +], +'go-project:sources/go/kubernetes/test/e2e/service_latency.go':[ +300, +], +'go-project:sources/go/kubernetes/test/e2e/storage/persistent_volumes-disruptive.go':[ +156, +174, +], +'go-project:sources/go/kubernetes/test/e2e/storage/volume_provisioning.go':[ +688, +], +'go-project:sources/go/kubernetes/test/e2e/storage/vsphere_volume_placement.go':[ +329, +], +'go-project:sources/go/kubernetes/test/e2e/ubernetes_lite.go':[ +74, +190, +380, +], +'go-project:sources/go/kubernetes/test/e2e_federation/namespace.go':[ +244, +255, +], +'go-project:sources/go/kubernetes/test/e2e_node/remote/remote.go':[ +163, +], +'go-project:sources/go/kubernetes/test/e2e_node/util.go':[ +94, +122, +], +'go-project:sources/go/kubernetes/test/images/clusterapi-tester/main.go':[ +60, +], +'go-project:sources/go/kubernetes/test/images/fakegitserver/gitserver.go':[ +24, +], +'go-project:sources/go/kubernetes/test/images/n-way-http/server.go':[ +42, +48, +], +'go-project:sources/go/kubernetes/test/images/netexec/netexec.go':[ +97, +121, +128, +137, +350, +], +'go-project:sources/go/kubernetes/test/images/network-tester/webserver.go':[ +204, +], +'go-project:sources/go/kubernetes/test/images/porter/porter.go':[ +59, +69, +], +'go-project:sources/go/kubernetes/test/integration/federation/framework/controller.go':[ +33, +], +'go-project:sources/go/kubernetes/test/integration/framework/master_utils.go':[ +158, +431, +443, +], +'go-project:sources/go/kubernetes/test/utils/runners.go':[ +774, +1003, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd221/pkg/fileutil/perallocate_unsupported.go':[ +26, +], +'go-project:sources/go/kubernetes/third_party/forked/etcd237/pkg/fileutil/perallocate_unsupported.go':[ +26, +], +'go-project:sources/go/kubernetes/third_party/forked/gonum/graph/simple/directed_acyclic.go':[ +34, +47, +63, +76, +], +'go-project:sources/go/kubernetes/third_party/forked/gonum/graph/simple/undirected.go':[ +83, +162, +185, +], +'project:samples/RedundantParentheses.go':[ +3, +], +'project:samples/TooManyParameters.go':[ +4, +7, +], +} diff --git a/its/ruling/src/test/resources/expected/go/old/go-S1751.json b/its/ruling/src/test/resources/expected/go/old/go-S1751.json new file mode 100644 index 00000000..b333e2b8 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/old/go-S1751.json @@ -0,0 +1,5 @@ +{ +'go-project:sources/go/kubernetes/staging/src/k8s.io/client-go/examples/create-update-delete-deployment/main.go':[ +157, +], +} diff --git a/its/ruling/src/test/resources/expected/go/old/go-S1994.json b/its/ruling/src/test/resources/expected/go/old/go-S1994.json new file mode 100644 index 00000000..dcc19866 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/old/go-S1994.json @@ -0,0 +1,5 @@ +{ +'go-project:sources/go/kubernetes/federation/pkg/federation-controller/util/planner/planner.go':[ +154, +], +} diff --git a/its/ruling/src/test/resources/expected/go/old/go-S3981.json b/its/ruling/src/test/resources/expected/go/old/go-S3981.json new file mode 100644 index 00000000..dfa14ca9 --- /dev/null +++ b/its/ruling/src/test/resources/expected/go/old/go-S3981.json @@ -0,0 +1,5 @@ +{ +'go-project:sources/go/kubernetes/federation/pkg/dnsprovider/tests/commontests.go':[ +138, +], +} diff --git a/its/ruling/src/test/resources/sources/go/ParsingError.go b/its/ruling/src/test/resources/sources/go/ParsingError.go new file mode 100644 index 00000000..051d1857 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/ParsingError.go @@ -0,0 +1 @@ +$!#@ diff --git a/its/ruling/src/test/resources/sources/go/S100.go b/its/ruling/src/test/resources/sources/go/S100.go new file mode 100644 index 00000000..e77c2285 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S100.go @@ -0,0 +1,16 @@ +// S100 Copyright +package samples + +func SUPERNAME() { + //Empty +} + +func _() { + //Empty +} + + +func _() int { + //Empty +} + diff --git a/its/ruling/src/test/resources/sources/go/S103.go b/its/ruling/src/test/resources/sources/go/S103.go new file mode 100644 index 00000000..34c1155b --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S103.go @@ -0,0 +1,8 @@ +// S103 +package samples + +func longLine() int { + x := 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + // Noncompliant@+1 {{Split this 122 characters long line (which is greater than 120 authorized).}} + return x + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +} diff --git a/its/ruling/src/test/resources/sources/go/S105.go b/its/ruling/src/test/resources/sources/go/S105.go new file mode 100644 index 00000000..5f7884a0 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S105.go @@ -0,0 +1,7 @@ +// S105 +package samples + +func three() int { + x := 3 + return x // Noncompliant +} diff --git a/its/ruling/src/test/resources/sources/go/S107.go b/its/ruling/src/test/resources/sources/go/S107.go new file mode 100644 index 00000000..7554b090 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S107.go @@ -0,0 +1,11 @@ +// S107 +package samples + +func functionWith8(p1 int, p2 int, p3 int, p4 int, p5 int, p6 int, p7 int, p8 int) { // Noncompliant {{Function has 8 parameters, which is greater than 7 authorized.}} +} + +func functionWith7(p1 int, p2 int, p3 int, p4 int, p5 int, p6 int, p7 int) { +} + +func (v Vertex) functionWithReceiver(p1 int, p2 int, p3 int, p4 int, p5 int, p6 int, p7 int) { +} diff --git a/its/ruling/src/test/resources/sources/go/S108.go b/its/ruling/src/test/resources/sources/go/S108.go new file mode 100644 index 00000000..3dc7cd24 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S108.go @@ -0,0 +1,27 @@ +// Copyright S108 +package samples + +func foo() { + for { } //Compliant, FN due to the fact that loop are not mapped to LoopTree + + n := 3 + for n < 10 { } //Compliant, this is equivalent to a while loop and not reported by the rule + + for i := 0; i < 10; i++ { } //NonCompliant + + for i := 0; i < 10; i++ { + //Compliant, this comment is inside + } + + tag := 1 + + switch tag { } //NonCompliant + + cond := false + + if cond { } //NonCompliant +} +//Next line compliant for rule 108 (but will trigger rule S1186) +func bar() { + +} diff --git a/its/ruling/src/test/resources/sources/go/S1110.go b/its/ruling/src/test/resources/sources/go/S1110.go new file mode 100644 index 00000000..03990fc8 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1110.go @@ -0,0 +1,5 @@ +package samples + +func test(int a) int { + return ((a + 1)) +} diff --git a/its/ruling/src/test/resources/sources/go/S1116.go b/its/ruling/src/test/resources/sources/go/S1116.go new file mode 100644 index 00000000..e39c391b --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1116.go @@ -0,0 +1,12 @@ +// S1116 +package samples + +import "fmt" + +func foo() { + + for ; i < 10; i++ { + ; // Noncompliant {{Remove this empty statement.}} + } + fmt.Println("doSomethingElse");; // Noncompliant {{Remove this empty statement.}} +} diff --git a/its/ruling/src/test/resources/sources/go/S1125.go b/its/ruling/src/test/resources/sources/go/S1125.go new file mode 100644 index 00000000..7ef3a579 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1125.go @@ -0,0 +1,7 @@ +package samples + +func test(flag bool) { + if flag && true { // Noncompliant + + } +} diff --git a/its/ruling/src/test/resources/sources/go/S1135.go b/its/ruling/src/test/resources/sources/go/S1135.go new file mode 100644 index 00000000..245f0ef9 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1135.go @@ -0,0 +1,8 @@ +// S1135 +package samples + +func todo() { + // Noncompliant@+1 + // Noncompliant@+1 {{Complete the task associated to this TODO comment.}} + // TODO x := 3 +} diff --git a/its/ruling/src/test/resources/sources/go/S1192.go b/its/ruling/src/test/resources/sources/go/S1192.go new file mode 100644 index 00000000..585bb85b --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1192.go @@ -0,0 +1,19 @@ +package samples + +// We do not expect any issue here because these strings are tags so cannot be replaced with a constant +type AccessRequest struct { + Id1 string "tag status" + Id2 string "tag status" + Id3 string "tag status" + Id4 string "tag status" + Id5 string "tag status" + Id6 string "tag status" + Id7 string "tag status" + Id8 string "tag status" +} + +var a = "my string literal" +var b = "my string literal" +var c = "my string literal" +var d = "my string literal" +var e = "my string literal" diff --git a/its/ruling/src/test/resources/sources/go/S122.go b/its/ruling/src/test/resources/sources/go/S122.go new file mode 100644 index 00000000..46a8c46f --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S122.go @@ -0,0 +1,27 @@ +// S122 +package samples + +func foo() { + + foo() // Compliant + + foo(); bar() // Noncompliant {{Reformat the code to have only one statement per line.}} + + if (bar() == bar()) { // Compliant + } + + f := func() { foo(); bar() } // Noncompliant + f() + + if bar() == 0 { foo() } // Compliant + if bar() == 0 { foo(); bar() } // Noncompliant + if bar() == 0 { foo() } else { bar() } // Compliant +} + +func bar() int { + return 0 +} + +func fn(x int) { + x = 42; // Compliant +} diff --git a/its/ruling/src/test/resources/sources/go/S1314.go b/its/ruling/src/test/resources/sources/go/S1314.go new file mode 100644 index 00000000..51eff16c --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1314.go @@ -0,0 +1,8 @@ +// Copyright +package samples + +func octalTest() int { + i := 42 // Compliant + octalLit := 042 // Noncompliant + j := 0.42 // Compliant +} diff --git a/its/ruling/src/test/resources/sources/go/S1656.go b/its/ruling/src/test/resources/sources/go/S1656.go new file mode 100644 index 00000000..377490bf --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1656.go @@ -0,0 +1,10 @@ +// S1656 +package samples + +type User struct { name string } + +func (user *User) rename(name string) { + name = name // Noncompliant {{Remove or correct this useless self-assignment.}} + user.name = name // Compliant + user.name = user.name // Noncompliant +} diff --git a/its/ruling/src/test/resources/sources/go/S1763.go b/its/ruling/src/test/resources/sources/go/S1763.go new file mode 100644 index 00000000..b715cd3c --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1763.go @@ -0,0 +1,47 @@ +// S1763 +package samples + +import "fmt" + +func sample() { + fmt.Print("hello") + return // Noncompliant + fmt.Print("world!") +} + + +func sample2() { + //Continue and break behave as in Java + sum := 0 +OUTER: + for i := 1; i < 500; i++ { + if i%2 != 0 { // skip odd numbers + continue // Noncompliant + fmt.Print("Dead code1!") + } + if i == 100 { // stop at 100 + break // Noncompliant + fmt.Print("Dead code2!") + } + if i%2 != 0 { // skip odd numbers + continue OUTER // Noncompliant + fmt.Print("Dead code3!") + } else { + fmt.Print("Not dead code!") // Compliant + } + sum += i + } +} + +func fn(x int) int { + if (x > 10) { + goto Foo + } + return 42 // Compliant +FOO: + return 21 +} + +func fn() int{ + return 0; // Compliant +} diff --git a/its/ruling/src/test/resources/sources/go/S1862.go b/its/ruling/src/test/resources/sources/go/S1862.go new file mode 100644 index 00000000..c0b93a95 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S1862.go @@ -0,0 +1,44 @@ +package samples + +func identicalIfConditions(cond bool) { + if cond { + + } else if cond { + + } + + a, b := 1, 2 + if a == b { + //Empty + } else if a == b { //NonCompliant + //Empty + } + + a, b := 1, 2 + if foo() { + //Empty + } else if foo() { //NonCompliant + //Empty + } + + tag := 1 + switch tag { + case 0: fmt.Println("1") + case 4, 5, 6, 7: fmt.Println("2") + case 0: fmt.Println("3") //NonCompliant + case 4, 5, 6, 7, 8: fmt.Println("2") + case 4, 5, 6, 7: fmt.Println("2") //NonCompliant + default: fmt.Println("default") + } + + switch { + case tag > 0: fmt.Println("1") + case tag == 0: fmt.Println("2") + case tag > 0: fmt.Println("3") //NonCompliant + default: fmt.Println("default") + } +} + +func foo() bool { + return true +} diff --git a/its/ruling/src/test/resources/sources/go/S2068.go b/its/ruling/src/test/resources/sources/go/S2068.go new file mode 100644 index 00000000..ae8f3a17 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S2068.go @@ -0,0 +1,37 @@ +package samples + +func noHardcodedCredentials() string { + password := "bar" + return password +} + +func hardcodedCredentials() { + params := "user=admin&password=Password123" // Sensitive + sqlserver := "pgsql:host=localhost port=5432 dbname=test user=postgres password=postgres" // Sensitive +} + +func foo() { + password := "Password" // Compliant + password = "[id='password']" // Compliant + password = "custom.password" // Compliant + password = "trustStorePassword" // Compliant + password = "connection.password" // Compliant + password = "/users/resetUserPassword" // Compliant +} + +func databaseQuery() { + query := "password=?" // Compliant + query = "password=:password" // Compliant + query = "password=:param" // Compliant + query = "password='" + password + "'" // Compliant + query = "password=%s" // Compliant + query = "password=%v" // Compliant +} + +func uriUserInfo() { + url := "scheme://user:azerty123@domain.com" // Sensitive + url = "scheme://user:@domain.com" // Compliant + url = "scheme://user@domain.com:80" // Compliant + url = "scheme://user@domain.com" // Compliant + url = "scheme://domain.com/user:azerty123" // Compliant +} diff --git a/its/ruling/src/test/resources/sources/go/S2757.go b/its/ruling/src/test/resources/sources/go/S2757.go new file mode 100644 index 00000000..2d1cdc49 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S2757.go @@ -0,0 +1,10 @@ +package samples + +func foo() int { + var target, num = -5, 3 + + target =- num // Noncompliant + target =+ num // Noncompliant + + return target +} diff --git a/its/ruling/src/test/resources/sources/go/S3923.go b/its/ruling/src/test/resources/sources/go/S3923.go new file mode 100644 index 00000000..04fd9251 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S3923.go @@ -0,0 +1,10 @@ +package samples + +func fooo(x int) int { + switch x { + case 1: + return 0 + default: + return 0 + } +} diff --git a/its/ruling/src/test/resources/sources/go/S4663.go b/its/ruling/src/test/resources/sources/go/S4663.go new file mode 100644 index 00000000..259fcf10 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/S4663.go @@ -0,0 +1,15 @@ +// S4663 +package samples + +func longLine() int { + // + // The following multi-lines comment is empty + /* + + */ + // The following multi-lines comment is NOT empty + /* + + Not empty + */ +} diff --git a/its/ruling/src/test/resources/sources/go/build.gradle b/its/ruling/src/test/resources/sources/go/build.gradle new file mode 100644 index 00000000..53c7be65 --- /dev/null +++ b/its/ruling/src/test/resources/sources/go/build.gradle @@ -0,0 +1,8 @@ +plugins { + id 'com.github.blindpirate.gogradle' version '0.8.1' +} + +golang { + packagePath = 'github.com/SonarSource/slang/its/ruling/source/go' + goVersion = '1.10' +} diff --git a/its/sources b/its/sources new file mode 160000 index 00000000..d6bc3f0d --- /dev/null +++ b/its/sources @@ -0,0 +1 @@ +Subproject commit d6bc3f0d4925a0f6a042aecc53cf66cb47ad0d76 diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..6a4d9cad --- /dev/null +++ b/settings.gradle @@ -0,0 +1,25 @@ +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +dependencyResolutionManagement { + repositories { + mavenCentral() + } +} + +rootProject.name = 'sonar-go' + +include 'slang-api' +include 'slang-testing' +include 'slang-antlr' +include 'slang-checks' +include 'slang-plugin' +include 'sonar-go-to-slang' +include 'sonar-go-plugin' +include 'checkstyle-import' +include 'its:plugin' +include 'its:ruling' diff --git a/slang-antlr/build.gradle b/slang-antlr/build.gradle new file mode 100644 index 00000000..689f81c3 --- /dev/null +++ b/slang-antlr/build.gradle @@ -0,0 +1,24 @@ +apply plugin: 'antlr' + +dependencies { + antlr ("org.antlr:antlr4:4.7.1") { + exclude group: 'org.glassfish', module: 'javax.json' + } + implementation project(':slang-api') + implementation 'org.sonarsource.analyzer-commons:sonar-analyzer-commons' + testImplementation project(':slang-testing') + testImplementation "org.junit.jupiter:junit-jupiter-api" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + testImplementation 'org.assertj:assertj-core' +} + +generateGrammarSource { + maxHeapSize = "64m" + arguments += ['-visitor', '-package', 'org.sonarsource.slang.parser'] +} + +sourceSets { + main { + antlr.srcDirs = [ "$projectDir/src/main/antlr4" ] + } +} diff --git a/slang-antlr/src/main/antlr4/org/sonarsource/slang/parser/SLang.g4 b/slang-antlr/src/main/antlr4/org/sonarsource/slang/parser/SLang.g4 new file mode 100644 index 00000000..3a20fa57 --- /dev/null +++ b/slang-antlr/src/main/antlr4/org/sonarsource/slang/parser/SLang.g4 @@ -0,0 +1,536 @@ +grammar SLang; + +slangFile + : packageDeclaration? importDeclaration* typeDeclaration* EOF + ; + +packageDeclaration + : PACKAGE identifier SEMICOLON + ; + +importDeclaration + : IMPORT identifier SEMICOLON + ; + +typeDeclaration + : classDeclaration + | methodDeclaration + | controlBlock SEMICOLON + ; + +classDeclaration + : annotation* CLASS identifier? LCURLY typeDeclaration* RCURLY + ; + +methodDeclaration + : annotation* methodModifier* methodHeader methodBody + ; + +annotation + : AT identifier (LPAREN annotationParameters RPAREN)? + ; + +annotationParameters + : annotationParameter (COMMA annotationParameter)* + ; + +annotationParameter + : literal + | identifier assignmentOperator annotationParameterList + ; + +annotationParameterList + : literal + | LCURLY literal (COMMA literal)* RCURLY + ; + +methodModifier + : PUBLIC + | PRIVATE + | OVERRIDE + | nativeExpression + ; + +methodHeader + : simpleType? FUN methodDeclarator + ; + +methodDeclarator + : identifier? LPAREN formalParameterList? RPAREN + ; + +formalParameterList + : formalParameters COMMA lastFormalParameter + | lastFormalParameter + | receiverParameter + ; + +formalParameters + : formalParameter (COMMA formalParameter)* + ; + +formalParameter + : annotation* parameterModifier* simpleType? variableDeclaratorId ('=' expression)? + ; + +parameterModifier + : nativeExpression + ; + +lastFormalParameter + : simpleType? ELLIPSIS variableDeclaratorId + | formalParameter + ; + +receiverParameter + : simpleType? (identifier DOT)? THIS + ; + +variableDeclaratorId + : identifier + ; + +methodBody + : block + | SEMICOLON + ; + +block + : LCURLY (statement semi)* (statement semi?)? RCURLY + ; + +statement + : declaration + | assignment + | expression + ; + +declaration + : annotation* simpleType? declarationModifier identifier ('=' expression)? + ; + +declarationModifier + : VAR + | VAL + ; + +assignment + : expression (assignmentOperator statement)+ + ; + +expression + : disjunction + ; + +disjunction + : conjunction (disjunctionOperator conjunction)* + ; + +conjunction + : equalityComparison (conjunctionOperator equalityComparison)* + ; + +equalityComparison + : comparison (equalityOperator comparison)* + ; + +comparison + : additiveExpression (comparisonOperator additiveExpression)* + ; + +additiveExpression + : multiplicativeExpression (additiveOperator multiplicativeExpression)* + ; + +multiplicativeExpression + : unaryExpression (multiplicativeOperator unaryExpression)* + ; + +unaryExpression + : unaryOperator unaryExpression + | atomicExpression + ; + +atomicExpression + : parenthesizedExpression + | nativeExpression + | methodDeclaration + | classDeclaration + | literal + | conditional + | loopExpression + | methodInvocation + | returnExpression + | expressionName + | tryExpression + | jumpExpression + | throwExpression + ; + +parenthesizedExpression + : LPAREN statement RPAREN + ; + +methodInvocation + : memberSelect LPAREN argumentList? RPAREN + ; + +memberSelect + : memberSelect DOT identifier + | identifier + ; + +argumentList + : statement (COMMA statement)* + ; + +expressionName + : memberSelect + ; + +conditional + : ifExpression + | matchExpression + ; + +ifExpression + : IF LPAREN statement RPAREN controlBlock (ELSE controlBlock)? + ; + +matchExpression + : MATCH LPAREN statement? RPAREN LCURLY matchCase* RCURLY + ; + +matchCase + : statement ARROW controlBlock? semi + | ELSE ARROW controlBlock? semi + ; + +loopExpression + : forLoop + | whileLoop + | doWhileLoop + ; + +forLoop + : FOR (LPAREN declaration RPAREN)? controlBlock + ; + +whileLoop + : WHILE LPAREN statement RPAREN controlBlock + ; + +doWhileLoop + : DO controlBlock WHILE LPAREN statement RPAREN + ; + +controlBlock + : block + | statement + ; + +tryExpression + : TRY block catchBlock* finallyBlock? + ; + +catchBlock + : CATCH LPAREN formalParameter? RPAREN block + ; + +finallyBlock + : FINALLY block + ; + +nativeExpression + : NATIVE LBRACK argumentList? RBRACK LCURLY nativeBlock* RCURLY + ; + +nativeBlock + : LBRACK (statement semi)* (statement semi?)? RBRACK + ; + +returnExpression + : RETURN statement? + ; + +throwExpression + : THROW statement? + ; + +jumpExpression + : breakExpression + | continueExpression + ; + +breakExpression + : BREAK label? + ; + +continueExpression + : CONTINUE label? + ; + +label + : identifier + ; + +/* Operators */ +multiplicativeOperator + : '*' | '/' | '%' + ; + +additiveOperator + : '+' | '-' + ; + +comparisonOperator + : '<' | '>' | '>=' | '<=' + ; + +equalityOperator + : '!=' | '==' + ; + +assignmentOperator + : '=' | '+=' + ; + +unaryOperator + : '!' | '++' | '--' | '+' | '-' + ; + +disjunctionOperator + : '||' + ; + +conjunctionOperator + : '&&' + ; + +// Type Hierarchy + +simpleType + : identifier + ; + +literal + : IntegerLiteral + | BooleanLiteral + | CharacterLiteral + | StringLiteral + | NullLiteral + ; + +semi + : NL+ + | SEMICOLON + | SEMICOLON NL+ + ; + +// LEXER + +identifier : Identifier; + +// Keywords + +CATCH : 'catch'; +CLASS : 'class'; +DO : 'do'; +ELSE : 'else'; +FINALLY : 'finally'; +FOR : 'for'; +FUN: 'fun'; +IF : 'if'; +MATCH : 'match'; +NATIVE : 'native'; +PRIVATE : 'private'; +PUBLIC : 'public'; +OVERRIDE: 'override'; +RETURN : 'return'; +THIS : 'this'; +TRY : 'try'; +VAL : 'val'; +VAR : 'var'; +WHILE : 'while'; +BREAK : 'break'; +CONTINUE: 'continue'; +IMPORT: 'import'; +PACKAGE: 'package'; +THROW: 'throw'; + + +// Integer Literals + +IntegerLiteral + : DecimalIntegerLiteral + | HexadecimalIntegerLiteral + | OctalIntegerLiteral + | BinaryIntegerLiteral + ; + +fragment +DecimalIntegerLiteral + : DecimalNumeral + ; + +fragment +DecimalNumeral + : '0' + | NonZeroDigit Digit* + ; + +fragment +Digit + : '0' + | NonZeroDigit + ; + +fragment +NonZeroDigit + : [1-9] + ; + +fragment +HexadecimalIntegerLiteral + : HexadecimalPrefix HexadecimalDigit+ + ; + +fragment +HexadecimalPrefix + : '0x' + | '0X' + ; + +fragment +HexadecimalDigit + : [0-9a-fA-F] + ; + +fragment +OctalIntegerLiteral + : OctalPrefix OctalDigit+ + ; + +fragment +OctalPrefix + : '0' + | '0o' + | '0O' + ; + +fragment +OctalDigit + : [0-7] + ; + +fragment +BinaryIntegerLiteral + : BinaryPrefix BinaryDigit+ + ; + +fragment +BinaryPrefix + : '0b' + | '0B' + ; + +fragment +BinaryDigit + : '0' + | '1' + ; + +// Boolean Literals + +BooleanLiteral + : 'true' + | 'false' + ; + +// Character Literals + +CharacterLiteral + : '\'' SingleCharacter '\'' + ; + +fragment +SingleCharacter + : ~['\\\r\n] + ; + +// String Literals + +StringLiteral + : '"' StringCharacters? '"' + ; + +fragment +StringCharacters + : StringCharacter+ + ; + +fragment +StringCharacter + : ~["\\\r\n] + ; + +// The Null Literal + +NullLiteral + : 'null' + ; + +// Separators + +ARROW : '->' ; +COMMA : ',' ; +DOT : '.' ; +ELLIPSIS : '...' ; +LBRACK : '[' ; +LCURLY : '{' ; +LPAREN : '(' ; +RBRACK : ']' ; +RCURLY : '}' ; +RPAREN : ')' ; +SEMICOLON : ';' ; +AT : '@' ; + +// Operators +GT : '>' ; +LT : '<' ; + +// Identifiers + +Identifier + : SLangLetter SLangLetterOrDigit* + ; + +fragment +SLangLetter + : [a-zA-Z$_] + ; + +fragment +SLangLetterOrDigit + : [a-zA-Z0-9$_] + ; + +// Whitespace and comments + +WS + : [ \t\r\n\u000C]+ -> skip + ; + +// COMMENTS +COMMENT + : '/*' .*? '*/' -> channel(1) + ; + +LINE_COMMENT + : '//' ~[\r\n]* -> channel(1) + ; + +NL + : '\u000D'? '\u000A' + ; + diff --git a/slang-antlr/src/main/java/org/sonarsource/slang/parser/ErrorStrategy.java b/slang-antlr/src/main/java/org/sonarsource/slang/parser/ErrorStrategy.java new file mode 100644 index 00000000..21a81741 --- /dev/null +++ b/slang-antlr/src/main/java/org/sonarsource/slang/parser/ErrorStrategy.java @@ -0,0 +1,75 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.parser; + +import org.antlr.v4.runtime.DefaultErrorStrategy; +import org.antlr.v4.runtime.Parser; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.misc.IntervalSet; +import org.sonarsource.slang.api.ParseException; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.impl.TextPointerImpl; + +public class ErrorStrategy extends DefaultErrorStrategy { + + @Override + public void reportError(Parser recognizer, RecognitionException e) { + Token t = recognizer.getCurrentToken(); + String errorMessage = String.format( + "Unexpected parsing error occurred. Last found valid token: %s at position %s:%s", + getTokenErrorDisplay(t), + t.getLine(), + t.getCharPositionInLine()); + TextPointer textPointer = new TextPointerImpl(t.getLine(), t.getCharPositionInLine()); + throw new ParseException(errorMessage, textPointer); + } + + @Override + public Token recoverInline(Parser recognizer) { + Token matchedSymbol = singleTokenDeletion(recognizer); + if (matchedSymbol != null) { + String errorMessage = String.format( + "Unexpected token found: %s at position %s:%s", + matchedSymbol.getText(), + matchedSymbol.getLine(), + matchedSymbol.getCharPositionInLine()); + throw new ParseException(errorMessage); + } + + singleTokenInsertion(recognizer); + + throw new ParseException("Unexpected parsing error"); + } + + @Override + protected void reportMissingToken(Parser recognizer) { + Token t = recognizer.getCurrentToken(); + IntervalSet expecting = getExpectedTokens(recognizer); + String errorMessage = String.format( + "missing %s before %s at position %s:%s", + expecting.toString(recognizer.getVocabulary()), + getTokenErrorDisplay(t), + t.getLine(), + t.getCharPositionInLine()); + throw new ParseException(errorMessage); + } + +} diff --git a/slang-antlr/src/main/java/org/sonarsource/slang/parser/SLangConverter.java b/slang-antlr/src/main/java/org/sonarsource/slang/parser/SLangConverter.java new file mode 100644 index 00000000..930b4b0c --- /dev/null +++ b/slang-antlr/src/main/java/org/sonarsource/slang/parser/SLangConverter.java @@ -0,0 +1,694 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.parser; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.Token; +import org.antlr.v4.runtime.tree.ParseTree; +import org.sonarsource.analyzer.commons.TokenLocation; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree.Operator; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.CatchTree; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.ModifierTree.Kind; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token.Type; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.impl.AnnotationImpl; +import org.sonarsource.slang.impl.AssignmentExpressionTreeImpl; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.BlockTreeImpl; +import org.sonarsource.slang.impl.CatchTreeImpl; +import org.sonarsource.slang.impl.ClassDeclarationTreeImpl; +import org.sonarsource.slang.impl.CommentImpl; +import org.sonarsource.slang.impl.ExceptionHandlingTreeImpl; +import org.sonarsource.slang.impl.FunctionDeclarationTreeImpl; +import org.sonarsource.slang.impl.FunctionInvocationTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.IfTreeImpl; +import org.sonarsource.slang.impl.ImportDeclarationTreeImpl; +import org.sonarsource.slang.impl.IntegerLiteralTreeImpl; +import org.sonarsource.slang.impl.JumpTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.LoopTreeImpl; +import org.sonarsource.slang.impl.MatchCaseTreeImpl; +import org.sonarsource.slang.impl.MatchTreeImpl; +import org.sonarsource.slang.impl.MemberSelectTreeImpl; +import org.sonarsource.slang.impl.ModifierTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.PackageDeclarationTreeImpl; +import org.sonarsource.slang.impl.ParameterTreeImpl; +import org.sonarsource.slang.impl.ParenthesizedExpressionTreeImpl; +import org.sonarsource.slang.impl.PlaceHolderTreeImpl; +import org.sonarsource.slang.impl.ReturnTreeImpl; +import org.sonarsource.slang.impl.StringLiteralTreeImpl; +import org.sonarsource.slang.impl.TextPointerImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.TextRanges; +import org.sonarsource.slang.impl.ThrowTreeImpl; +import org.sonarsource.slang.impl.TokenImpl; +import org.sonarsource.slang.impl.TopLevelTreeImpl; +import org.sonarsource.slang.impl.TreeMetaDataProvider; +import org.sonarsource.slang.impl.UnaryExpressionTreeImpl; +import org.sonarsource.slang.impl.VariableDeclarationTreeImpl; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; +import static org.sonarsource.slang.api.LoopTree.LoopKind.DOWHILE; +import static org.sonarsource.slang.api.LoopTree.LoopKind.FOR; +import static org.sonarsource.slang.api.LoopTree.LoopKind.WHILE; +import static org.sonarsource.slang.api.ModifierTree.Kind.OVERRIDE; +import static org.sonarsource.slang.api.ModifierTree.Kind.PRIVATE; +import static org.sonarsource.slang.api.ModifierTree.Kind.PUBLIC; +import static org.sonarsource.slang.api.UnaryExpressionTree.Operator.DECREMENT; +import static org.sonarsource.slang.api.UnaryExpressionTree.Operator.INCREMENT; +import static org.sonarsource.slang.api.UnaryExpressionTree.Operator.MINUS; +import static org.sonarsource.slang.api.UnaryExpressionTree.Operator.NEGATE; +import static org.sonarsource.slang.api.UnaryExpressionTree.Operator.PLUS; + +public class SLangConverter implements ASTConverter { + + private static final Set KEYWORD_TOKEN_TYPES = new HashSet<>(Arrays.asList( + SLangParser.ELSE, + SLangParser.FUN, + SLangParser.IF, + SLangParser.MATCH, + SLangParser.NATIVE, + SLangParser.PRIVATE, + SLangParser.PUBLIC, + SLangParser.RETURN, + SLangParser.THIS)); + + @Override + public Tree parse(String slangCode) { + CommonTokenStream antlrTokens = getTokenStream(slangCode); + + List comments = new ArrayList<>(); + List tokens = new ArrayList<>(); + + for (int index = 0; index < antlrTokens.size(); index++) { + Token token = antlrTokens.get(index); + TextRange textRange = getSlangTextRange(token); + if (token.getChannel() == 1) { + comments.add(comment(token, textRange)); + } else { + Type type = Type.OTHER; + if (KEYWORD_TOKEN_TYPES.contains(token.getType())) { + type = Type.KEYWORD; + } else if (token.getType() == SLangParser.StringLiteral) { + type = Type.STRING_LITERAL; + } + tokens.add(new TokenImpl(textRange, token.getText(), type)); + } + } + + // We can not re-use the same SlangParser to visit the tree multiples times, we have to parse a second time for annotations. + // This is not optimal, but this converter is used only for tests, it won't impact production's performance. + SLangParser parserAnnotation = new SLangParser(getTokenStream(slangCode)); + parserAnnotation.setErrorHandler(new ErrorStrategy()); + + SlangParseTreeAnnotationsVisitor annotationsVisitor = new SlangParseTreeAnnotationsVisitor(); + annotationsVisitor.visit(parserAnnotation.slangFile()); + + SLangParser parser = new SLangParser(antlrTokens); + parser.setErrorHandler(new ErrorStrategy()); + + SLangParseTreeVisitor slangVisitor = new SLangParseTreeVisitor(comments, tokens, annotationsVisitor.annotations); + return slangVisitor.visit(parser.slangFile()); + } + + private static CommonTokenStream getTokenStream(String slangCode) { + SLangLexer lexer = new SLangLexer(CharStreams.fromString(slangCode)); + CommonTokenStream antlrTokens = new CommonTokenStream(lexer); + antlrTokens.fill(); + return antlrTokens; + } + + private static CommentImpl comment(Token token, TextRange range) { + String text = token.getText(); + String contentText; + TextPointer contentEnd; + if (text.startsWith("//")) { + contentText = text.substring(2); + contentEnd = range.end(); + } else { + contentText = text.substring(2, text.length() - 2); + contentEnd = new TextPointerImpl(range.end().line(), range.end().lineOffset() - 2); + } + TextPointer contentStart = new TextPointerImpl(range.start().line(), range.start().lineOffset() + 2); + return new CommentImpl(token.getText(), contentText, range, new TextRangeImpl(contentStart, contentEnd)); + } + + private static final Map UNARY_OPERATOR_MAP = unaryOperatorMap(); + private static final Map BINARY_OPERATOR_MAP = binaryOperatorMap(); + private static final Map ASSIGNMENT_OPERATOR_MAP = assignmentOperatorMap(); + + private static Map unaryOperatorMap() { + Map map = new HashMap<>(); + map.put("!", NEGATE); + map.put("+", PLUS); + map.put("-", MINUS); + map.put("++", INCREMENT); + map.put("--", DECREMENT); + return Collections.unmodifiableMap(map); + } + + private static Map binaryOperatorMap() { + Map map = new HashMap<>(); + map.put("&&", Operator.CONDITIONAL_AND); + map.put("||", Operator.CONDITIONAL_OR); + map.put(">", Operator.GREATER_THAN); + map.put(">=", Operator.GREATER_THAN_OR_EQUAL_TO); + map.put("<", Operator.LESS_THAN); + map.put("<=", Operator.LESS_THAN_OR_EQUAL_TO); + map.put("==", Operator.EQUAL_TO); + map.put("!=", Operator.NOT_EQUAL_TO); + map.put("+", Operator.PLUS); + map.put("-", Operator.MINUS); + map.put("*", Operator.TIMES); + map.put("/", Operator.DIVIDED_BY); + return Collections.unmodifiableMap(map); + } + + private static Map assignmentOperatorMap() { + Map map = new HashMap<>(); + map.put("=", AssignmentExpressionTree.Operator.EQUAL); + map.put("+=", AssignmentExpressionTree.Operator.PLUS_EQUAL); + return Collections.unmodifiableMap(map); + } + + private static TextRange getSlangTextRange(Token matchToken) { + TokenLocation location = new TokenLocation(matchToken.getLine(), matchToken.getCharPositionInLine(), matchToken.getText()); + return new TextRangeImpl(location.startLine(), location.startLineOffset(), location.endLine(), location.endLineOffset()); + } + + private static class SlangParseTreeAnnotationsVisitor extends SLangBaseVisitor { + + List annotations = new ArrayList<>(); + + @Override + public Tree visitAnnotation(SLangParser.AnnotationContext ctx) { + String simpleName = ctx.identifier().Identifier().toString(); + + List argumentsText = new ArrayList<>(); + SLangParser.AnnotationParametersContext argument = ctx.annotationParameters(); + if (argument != null) { + argumentsText.addAll(argument.annotationParameter().stream().map(RuleContext::getText).collect(Collectors.toList())); + } + + annotations.add(new AnnotationImpl(simpleName, argumentsText, + new TextRangeImpl(SLangParseTreeVisitor.startOf(ctx.start), SLangParseTreeVisitor.endOf(ctx.stop)))); + + return super.visitAnnotation(ctx); + } + } + + private static class SLangParseTreeVisitor extends SLangBaseVisitor { + + private final TreeMetaDataProvider metaDataProvider; + + public SLangParseTreeVisitor(List comments, List tokens, List annotations) { + metaDataProvider = new TreeMetaDataProvider(comments, tokens, annotations); + } + + @Override + public Tree visitSlangFile(SLangParser.SlangFileContext ctx) { + // Special case for text range here, as last token is which has length 5, so we only go up to the start of the token + TextRangeImpl textRange = new TextRangeImpl(startOf(ctx.start), new TextPointerImpl(ctx.stop.getLine(), ctx.stop.getCharPositionInLine())); + List typeDeclarations = list(ctx.typeDeclaration()); + List allDeclarations = new ArrayList<>(); + if (ctx.packageDeclaration() != null) { + allDeclarations.add(visit(ctx.packageDeclaration())); + } + allDeclarations.addAll(list(ctx.importDeclaration())); + allDeclarations.addAll(typeDeclarations); + org.sonarsource.slang.api.Token firstCpdToken = typeDeclarations.isEmpty() ? null : typeDeclarations.get(0).metaData().tokens().get(0); + return new TopLevelTreeImpl(meta(textRange), allDeclarations, metaDataProvider.allComments(), firstCpdToken); + } + + @Override + public Tree visitPackageDeclaration(SLangParser.PackageDeclarationContext ctx) { + return new PackageDeclarationTreeImpl(meta(ctx), singletonList(visit(ctx.identifier()))); + } + + @Override + public Tree visitImportDeclaration(SLangParser.ImportDeclarationContext ctx) { + return new ImportDeclarationTreeImpl(meta(ctx), singletonList(visit(ctx.identifier()))); + } + + @Override + public Tree visitTypeDeclaration(SLangParser.TypeDeclarationContext ctx) { + if (ctx.methodDeclaration() != null) { + return visit(ctx.methodDeclaration()); + } else if (ctx.classDeclaration() != null) { + return visit(ctx.classDeclaration()); + } else { + return visit(ctx.controlBlock()); + } + } + + @Override + public Tree visitClassDeclaration(SLangParser.ClassDeclarationContext ctx) { + List children = new ArrayList<>(); + IdentifierTree identifier = null; + + if (ctx.identifier() != null) { + identifier = (IdentifierTree) visit(ctx.identifier()); + children.add(identifier); + } + + children.addAll(list(ctx.typeDeclaration())); + + NativeTree classDecl = new NativeTreeImpl(meta(ctx), new SNativeKind(ctx), children); + return new ClassDeclarationTreeImpl(meta(ctx), identifier, classDecl); + } + + @Override + public Tree visitNativeExpression(SLangParser.NativeExpressionContext ctx) { + return nativeTree(ctx, ctx.nativeBlock()); + } + + @Override + public Tree visitParenthesizedExpression(SLangParser.ParenthesizedExpressionContext ctx) { + return new ParenthesizedExpressionTreeImpl( + meta(ctx), + visit(ctx.statement()), + toSlangToken(ctx.LPAREN().getSymbol()), + toSlangToken(ctx.RPAREN().getSymbol())); + } + + @Override + public Tree visitMethodDeclaration(SLangParser.MethodDeclarationContext ctx) { + List modifiers = list(ctx.methodModifier()); + Tree returnType = null; + IdentifierTree name = null; + SLangParser.MethodHeaderContext methodHeaderContext = ctx.methodHeader(); + SLangParser.SimpleTypeContext resultContext = methodHeaderContext.simpleType(); + SLangParser.IdentifierContext identifier = methodHeaderContext.methodDeclarator().identifier(); + if (resultContext != null) { + returnType = new IdentifierTreeImpl(meta(resultContext), resultContext.getText()); + } + boolean isConstructor = false; + if (identifier != null) { + name = (IdentifierTree) visit(identifier); + isConstructor = "constructor".equals(name.name()); + } + + List convertedParameters = new ArrayList<>(); + SLangParser.FormalParameterListContext formalParameterListContext = methodHeaderContext.methodDeclarator().formalParameterList(); + if (formalParameterListContext != null) { + SLangParser.FormalParametersContext formalParameters = formalParameterListContext.formalParameters(); + if (formalParameters != null) { + convertedParameters.addAll(list(formalParameters.formalParameter())); + } + convertedParameters.add(visit(formalParameterListContext.lastFormalParameter())); + } + + return new FunctionDeclarationTreeImpl(meta(ctx), modifiers, isConstructor, returnType, name, convertedParameters, (BlockTree) visit(ctx.methodBody()), emptyList()); + } + + @Override + public Tree visitMethodModifier(SLangParser.MethodModifierContext ctx) { + Kind modifierKind = PUBLIC; + if (ctx.PRIVATE() != null) { + modifierKind = PRIVATE; + } else if (ctx.OVERRIDE() != null) { + modifierKind = OVERRIDE; + } else if(ctx.nativeExpression() != null) { + return visit(ctx.nativeExpression()); + } + return new ModifierTreeImpl(meta(ctx), modifierKind); + } + + @Override + public Tree visitMethodInvocation(SLangParser.MethodInvocationContext ctx) { + List arguments = new ArrayList<>(); + SLangParser.ArgumentListContext argumentListContext = ctx.argumentList(); + if (argumentListContext != null) { + arguments.addAll(list(argumentListContext.statement())); + } + + return new FunctionInvocationTreeImpl(meta(ctx), visit(ctx.memberSelect()), arguments); + } + + @Override + public Tree visitMemberSelect(SLangParser.MemberSelectContext ctx) { + Tree id = visit(ctx.identifier()); + if (ctx.memberSelect() != null) { + IdentifierTree identifier = (IdentifierTree) id; + return new MemberSelectTreeImpl(meta(ctx), visit(ctx.memberSelect()), identifier); + } else { + return id; + } + } + + @Override + public Tree visitMethodBody(SLangParser.MethodBodyContext ctx) { + if (ctx.SEMICOLON() != null) { + return null; + } + return visit(ctx.block()); + } + + @Override + public Tree visitFormalParameter(SLangParser.FormalParameterContext ctx) { + IdentifierTree tree = (IdentifierTree) visit(ctx.variableDeclaratorId().identifier()); + Tree type = null; + Tree defaultValue = null; + List modifiers = list(ctx.parameterModifier()); + + if (ctx.simpleType() != null) { + type = new IdentifierTreeImpl(meta(ctx.simpleType()), ctx.simpleType().getText()); + } + if(ctx.expression() != null) { + defaultValue = visit(ctx.expression()); + } + return new ParameterTreeImpl(meta(ctx), tree, type, defaultValue, modifiers); + } + + @Override + public Tree visitLastFormalParameter(SLangParser.LastFormalParameterContext ctx) { + return visit(ctx.formalParameter()); + } + + @Override + public Tree visitDeclaration(SLangParser.DeclarationContext ctx) { + IdentifierTree identifier = (IdentifierTree) visit(ctx.identifier()); + Tree type = null; + if (ctx.simpleType() != null) { + type = new IdentifierTreeImpl(meta(ctx.simpleType()), ctx.simpleType().getText()); + } + Tree initializer = null; + if (ctx.expression() != null) { + initializer = visit(ctx.expression()); + } + + boolean isVal = ctx.declarationModifier().VAL() != null; + return new VariableDeclarationTreeImpl(meta(ctx), identifier, type, initializer, isVal); + } + + @Override + public Tree visitBlock(SLangParser.BlockContext ctx) { + return new BlockTreeImpl(meta(ctx), list(ctx.statement())); + } + + @Override + public Tree visitIfExpression(SLangParser.IfExpressionContext ctx) { + org.sonarsource.slang.api.Token ifToken = toSlangToken(ctx.IF().getSymbol()); + org.sonarsource.slang.api.Token elseToken = null; + Tree elseBranch = null; + if (ctx.controlBlock().size() > 1) { + elseBranch = visit(ctx.controlBlock(1)); + elseToken = toSlangToken(ctx.ELSE().getSymbol()); + } + Tree thenBranch = visit(ctx.controlBlock(0)); + return new IfTreeImpl( + meta(ctx), + visit(ctx.statement()), + thenBranch, + elseBranch, + ifToken, + elseToken); + } + + @Override + public Tree visitMatchExpression(SLangParser.MatchExpressionContext ctx) { + List cases = new ArrayList<>(); + for (SLangParser.MatchCaseContext matchCaseContext : ctx.matchCase()) { + cases.add((MatchCaseTree) visit(matchCaseContext)); + } + TreeMetaData meta = meta(ctx); + Tree expression = ctx.statement() == null ? null : visit(ctx.statement()); + return new MatchTreeImpl( + meta, + expression, + cases, + toSlangToken(ctx.MATCH().getSymbol())); + } + + @Override + public Tree visitMatchCase(SLangParser.MatchCaseContext ctx) { + Tree expression = ctx.statement() == null ? null : visit(ctx.statement()); + Tree body = ctx.controlBlock() == null ? null : visit(ctx.controlBlock()); + return new MatchCaseTreeImpl(meta(ctx), expression, body); + } + + @Override + public Tree visitForLoop(SLangParser.ForLoopContext ctx) { + Tree condition = ctx.declaration() == null ? null : visit(ctx.declaration()); + Tree body = visit(ctx.controlBlock()); + return new LoopTreeImpl(meta(ctx), condition, body, FOR, toSlangToken(ctx.FOR().getSymbol())); + } + + @Override + public Tree visitWhileLoop(SLangParser.WhileLoopContext ctx) { + Tree condition = visit(ctx.statement()); + Tree body = visit(ctx.controlBlock()); + return new LoopTreeImpl(meta(ctx), condition, body, WHILE, toSlangToken(ctx.WHILE().getSymbol())); + } + + @Override + public Tree visitDoWhileLoop(SLangParser.DoWhileLoopContext ctx) { + Tree condition = visit(ctx.statement()); + Tree body = visit(ctx.controlBlock()); + return new LoopTreeImpl(meta(ctx), condition, body, DOWHILE, toSlangToken(ctx.DO().getSymbol())); + } + + @Override + public Tree visitCatchBlock(SLangParser.CatchBlockContext ctx) { + ParameterTree parameter = ctx.formalParameter() == null ? null : (ParameterTree) visit(ctx.formalParameter()); + Tree body = visit(ctx.block()); + return new CatchTreeImpl(meta(ctx), parameter, body, toSlangToken(ctx.CATCH().getSymbol())); + } + + @Override + public Tree visitTryExpression(SLangParser.TryExpressionContext ctx) { + Tree tryBlock = visit(ctx.block()); + List catchTreeList = new ArrayList<>(); + for (SLangParser.CatchBlockContext catchBlockContext : ctx.catchBlock()) { + catchTreeList.add((CatchTree) visit(catchBlockContext)); + } + org.sonarsource.slang.api.Token tryToken = toSlangToken(ctx.TRY().getSymbol()); + Tree finallyBlock = ctx.finallyBlock() == null ? null : visit(ctx.finallyBlock()); + return new ExceptionHandlingTreeImpl(meta(ctx), tryBlock, tryToken, catchTreeList, finallyBlock); + } + + @Override + public Tree visitNativeBlock(SLangParser.NativeBlockContext ctx) { + return nativeTree(ctx, ctx.statement()); + } + + @Override + public Tree visitAssignment(SLangParser.AssignmentContext ctx) { + Tree leftHandSide = visit(ctx.expression()); + Tree statementOrExpression = assignmentTree(ctx.statement(), ctx.assignmentOperator()); + AssignmentExpressionTree.Operator operator = ASSIGNMENT_OPERATOR_MAP.get(ctx.assignmentOperator(0).getText()); + return new AssignmentExpressionTreeImpl(meta(ctx), operator, leftHandSide, statementOrExpression); + } + + @Override + public Tree visitDisjunction(SLangParser.DisjunctionContext ctx) { + return binaryTree(ctx.conjunction(), ctx.disjunctionOperator()); + } + + @Override + public Tree visitConjunction(SLangParser.ConjunctionContext ctx) { + return binaryTree(ctx.equalityComparison(), ctx.conjunctionOperator()); + } + + @Override + public Tree visitEqualityComparison(SLangParser.EqualityComparisonContext ctx) { + return binaryTree(ctx.comparison(), ctx.equalityOperator()); + } + + @Override + public Tree visitComparison(SLangParser.ComparisonContext ctx) { + return binaryTree(ctx.additiveExpression(), ctx.comparisonOperator()); + } + + @Override + public Tree visitAdditiveExpression(SLangParser.AdditiveExpressionContext ctx) { + return binaryTree(ctx.multiplicativeExpression(), ctx.additiveOperator()); + } + + @Override + public Tree visitMultiplicativeExpression(SLangParser.MultiplicativeExpressionContext ctx) { + return binaryTree(ctx.unaryExpression(), ctx.multiplicativeOperator()); + } + + @Override + public Tree visitUnaryExpression(SLangParser.UnaryExpressionContext ctx) { + if (ctx.unaryOperator() == null) { + return visit(ctx.atomicExpression()); + } else { + Tree operand = visit(ctx.unaryExpression()); + return new UnaryExpressionTreeImpl(meta(ctx), UNARY_OPERATOR_MAP.get(ctx.unaryOperator().getText()), operand); + } + } + + @Override + public Tree visitLiteral(SLangParser.LiteralContext ctx) { + if (ctx.StringLiteral() != null) { + return new StringLiteralTreeImpl(meta(ctx), ctx.getText()); + } else if (ctx.IntegerLiteral() != null) { + return new IntegerLiteralTreeImpl(meta(ctx), ctx.getText()); + } else { + return new LiteralTreeImpl(meta(ctx), ctx.getText()); + } + } + + @Override + public Tree visitIdentifier(SLangParser.IdentifierContext ctx) { + // corner case where a specific syntax is not supported as identifier + if("__".equals(ctx.getText())) { + return null; + } else if("_".equals(ctx.getText())) { + return new PlaceHolderTreeImpl(meta(ctx), toSlangToken(ctx.getStart())); + } else { + return new IdentifierTreeImpl(meta(ctx), ctx.getText()); + } + } + + @Override + public Tree visitBreakExpression(SLangParser.BreakExpressionContext ctx) { + IdentifierTree label = null; + if (ctx.label() != null) { + label = (IdentifierTree) visit(ctx.label()); + } + return new JumpTreeImpl(meta(ctx), toSlangToken(ctx.BREAK().getSymbol()), JumpTree.JumpKind.BREAK, label); + } + + @Override + public Tree visitContinueExpression(SLangParser.ContinueExpressionContext ctx) { + IdentifierTree label = null; + if (ctx.label() != null) { + label = (IdentifierTree) visit(ctx.label()); + } + return new JumpTreeImpl(meta(ctx), toSlangToken(ctx.CONTINUE().getSymbol()), JumpTree.JumpKind.CONTINUE, label); + } + + @Override + public Tree visitReturnExpression(SLangParser.ReturnExpressionContext ctx) { + Tree returnBody = null; + if (ctx.statement() != null) { + returnBody = visit(ctx.statement()); + } + return new ReturnTreeImpl(meta(ctx), toSlangToken(ctx.RETURN().getSymbol()), returnBody); + } + + @Override + public Tree visitThrowExpression(SLangParser.ThrowExpressionContext ctx) { + Tree throwBody = null; + if (ctx.statement() != null) { + throwBody = visit(ctx.statement()); + } + return new ThrowTreeImpl(meta(ctx), toSlangToken(ctx.THROW().getSymbol()), throwBody); + } + + private static TextPointer startOf(Token token) { + return new TextPointerImpl(token.getLine(), token.getCharPositionInLine()); + } + + private static TextPointer endOf(Token token) { + return new TextPointerImpl(token.getLine(), token.getCharPositionInLine() + token.getText().length()); + } + + private TreeMetaData meta(ParserRuleContext ctx) { + return meta(new TextRangeImpl(startOf(ctx.start), endOf(ctx.stop))); + } + + private TreeMetaData meta(Tree first, Tree last) { + return meta(new TextRangeImpl(first.metaData().textRange().start(), last.metaData().textRange().end())); + } + + private TreeMetaData meta(TextRange textRange) { + return metaDataProvider.metaData(textRange); + } + + private NativeTree nativeTree(ParserRuleContext ctx, List rawChildren) { + List children = list(rawChildren); + return new NativeTreeImpl(meta(ctx), new SNativeKind(ctx), children); + } + + private List list(List rawChildren) { + return rawChildren + .stream() + .map(this::visit) + .collect(toList()); + } + + private Tree binaryTree(List operands, List operators) { + Tree result = visit(operands.get(operands.size() - 1)); + for (int i = operands.size() - 2; i >= 0; i--) { + Tree left = visit(operands.get(i)); + Operator operator = BINARY_OPERATOR_MAP.get(operators.get(i).getText()); + result = new BinaryExpressionTreeImpl(meta(left, result), operator, operatorToken(operators.get(i)), left, result); + } + return result; + } + + private Tree assignmentTree(List expressions, List operators) { + Tree result = visit(expressions.get(expressions.size() - 1)); + for (int i = expressions.size() - 2; i >= 0; i--) { + Tree left = visit(expressions.get(i)); + AssignmentExpressionTree.Operator operator = ASSIGNMENT_OPERATOR_MAP.get(operators.get(i).getText()); + result = new AssignmentExpressionTreeImpl(meta(left, result), operator, left, result); + } + return result; + } + + private static org.sonarsource.slang.api.Token toSlangToken(Token antlrToken) { + TextRange textRange = getSlangTextRange(antlrToken); + return new TokenImpl(textRange, antlrToken.getText(), Type.KEYWORD); + } + + private static org.sonarsource.slang.api.Token operatorToken(ParserRuleContext parserRuleContext) { + TextRange textRange = TextRanges.merge(Arrays.asList( + getSlangTextRange(parserRuleContext.start), + getSlangTextRange(parserRuleContext.stop))); + return new TokenImpl(textRange, parserRuleContext.getText(), Type.OTHER); + } + + } +} diff --git a/slang-antlr/src/main/java/org/sonarsource/slang/parser/SNativeKind.java b/slang-antlr/src/main/java/org/sonarsource/slang/parser/SNativeKind.java new file mode 100644 index 00000000..9f8f9802 --- /dev/null +++ b/slang-antlr/src/main/java/org/sonarsource/slang/parser/SNativeKind.java @@ -0,0 +1,76 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.parser; + +import org.sonarsource.slang.api.NativeKind; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.antlr.v4.runtime.ParserRuleContext; + +public class SNativeKind implements NativeKind { + private final Class ctxClass; + private final List differentiators; + + public SNativeKind(ParserRuleContext ctx) { + ctxClass = ctx.getClass(); + differentiators = Collections.emptyList(); + } + + public SNativeKind(ParserRuleContext ctx, Object... differentiatorObjs) { + ctxClass = ctx.getClass(); + differentiators = Arrays.asList(differentiatorObjs); + } + + public SNativeKind(Class ctxClass, Object... differentiatorObjs) { + this.ctxClass = ctxClass; + differentiators = Arrays.asList(differentiatorObjs); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SNativeKind that = (SNativeKind) o; + return Objects.equals(ctxClass, that.ctxClass) && + Objects.equals(differentiators, that.differentiators); + } + + @Override + public int hashCode() { + return Objects.hash(ctxClass, differentiators); + } + + @Override + public String toString() { + if (differentiators.isEmpty()) { + return ctxClass.getSimpleName(); + } else { + return ctxClass.getSimpleName() + + differentiators.stream().map(Object::toString).collect(Collectors.joining(", ", "[", "]")); + } + } +} diff --git a/slang-antlr/src/main/java/org/sonarsource/slang/parser/SlangCodeVerifier.java b/slang-antlr/src/main/java/org/sonarsource/slang/parser/SlangCodeVerifier.java new file mode 100644 index 00000000..19ff87d4 --- /dev/null +++ b/slang-antlr/src/main/java/org/sonarsource/slang/parser/SlangCodeVerifier.java @@ -0,0 +1,67 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.parser; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.CodeVerifier; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.Tree; + +public class SlangCodeVerifier implements CodeVerifier { + @Override + public boolean containsCode(String content) { + SLangConverter sLangConverter = new SLangConverter(); + Tree tree; + try { + tree = sLangConverter.parse(content); + } catch (Exception e) { + tree = null; + } + + return tree != null && !isSimpleExpression(tree); + } + + private static boolean isSimpleExpression(Tree tree) { + long all = tree.descendants().count(); + if (all == 0) { + return true; + } + long remaining = tree.descendants() + .filter(element -> !(element instanceof IdentifierTree || + element instanceof LiteralTree || + simpleBinaryExpressionTree(element))) + .count(); + + double detectedCodeRatio = (double) remaining / all; + return detectedCodeRatio < 0.3; + + } + + private static boolean simpleBinaryExpressionTree(Tree element) { + if (element instanceof BinaryExpressionTree) { + BinaryExpressionTree expression = (BinaryExpressionTree) element; + return expression.operator().equals(BinaryExpressionTree.Operator.PLUS) + || expression.operator().equals(BinaryExpressionTree.Operator.MINUS) + || expression.operator().equals(BinaryExpressionTree.Operator.DIVIDED_BY); + } + return false; + } +} diff --git a/slang-antlr/src/main/java/org/sonarsource/slang/parser/package-info.java b/slang-antlr/src/main/java/org/sonarsource/slang/parser/package-info.java new file mode 100644 index 00000000..82b73481 --- /dev/null +++ b/slang-antlr/src/main/java/org/sonarsource/slang/parser/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.parser; diff --git a/slang-antlr/src/test/java/org/sonarsource/slang/antlr/SLangConverterTest.java b/slang-antlr/src/test/java/org/sonarsource/slang/antlr/SLangConverterTest.java new file mode 100644 index 00000000..e5266d4e --- /dev/null +++ b/slang-antlr/src/test/java/org/sonarsource/slang/antlr/SLangConverterTest.java @@ -0,0 +1,882 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.antlr; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree.Operator; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.ExceptionHandlingTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.FunctionInvocationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.ImportDeclarationTree; +import org.sonarsource.slang.api.IntegerLiteralTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.MemberSelectTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.PackageDeclarationTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.ParenthesizedExpressionTree; +import org.sonarsource.slang.api.ParseException; +import org.sonarsource.slang.api.PlaceHolderTree; +import org.sonarsource.slang.api.ReturnTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.impl.FunctionDeclarationTreeImpl; +import org.sonarsource.slang.impl.ModifierTreeImpl; +import org.sonarsource.slang.parser.SLangConverter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.GREATER_THAN; +import static org.sonarsource.slang.api.LoopTree.LoopKind.DOWHILE; +import static org.sonarsource.slang.api.LoopTree.LoopKind.FOR; +import static org.sonarsource.slang.api.LoopTree.LoopKind.WHILE; +import static org.sonarsource.slang.api.ModifierTree.Kind.OVERRIDE; +import static org.sonarsource.slang.api.ModifierTree.Kind.PRIVATE; +import static org.sonarsource.slang.api.ModifierTree.Kind.PUBLIC; +import static org.sonarsource.slang.api.Token.Type.KEYWORD; +import static org.sonarsource.slang.api.Token.Type.OTHER; +import static org.sonarsource.slang.api.Token.Type.STRING_LITERAL; +import static org.sonarsource.slang.testing.RangeAssert.assertRange; +import static org.sonarsource.slang.testing.TreeAssert.assertTree; + +class SLangConverterTest { + + private SLangConverter converter = new SLangConverter(); + + @Test + void top_level_block() { + Tree tree = converter.parse("{ 2; };").children().get(0); + assertTree(tree).isBlock(LiteralTree.class); + } + + @Test + void first_cpd_token() { + TopLevelTree tree = (TopLevelTree) converter.parse("package abc; import x; import y; 42;"); + assertThat(tree.firstCpdToken().text()).isEqualTo("42"); + } + + @Test + void package_declaration() { + Tree tree = converter.parse("package abc;").children().get(0); + assertThat(tree).isInstanceOf(PackageDeclarationTree.class); + assertThat(tree.children()).hasSize(1); + assertTree(tree.children().get(0)).isIdentifier("abc"); + } + + @Test + void import_declaration() { + Tree tree = converter.parse("import abc;").children().get(0); + assertThat(tree).isInstanceOf(ImportDeclarationTree.class); + assertThat(tree.children()).hasSize(1); + assertTree(tree.children().get(0)).isIdentifier("abc"); + } + + @Test + void simple_binary_expression() { + BinaryExpressionTree binary = parseBinary("x + 1;"); + assertTree(binary).isBinaryExpression(Operator.PLUS).hasTextRange(1, 0, 1, 5); + assertTree(binary.leftOperand()).isIdentifier("x").hasTextRange(1, 0, 1, 1); + assertTree(binary.rightOperand()).isLiteral("1").hasTextRange(1, 4, 1, 5); + assertThat(binary.operatorToken().text()).isEqualTo("+"); + } + + @Test + void simple_unary_expression() { + BinaryExpressionTree binary = parseBinary("!!x && !(y && z);"); + Tree left = binary.leftOperand(); + Tree right = binary.rightOperand(); + + assertTree(left).isUnaryExpression(UnaryExpressionTree.Operator.NEGATE); + assertTree(right).isUnaryExpression(UnaryExpressionTree.Operator.NEGATE); + + UnaryExpressionTree unaryLeft = (UnaryExpressionTree) left; + UnaryExpressionTree unaryRight = (UnaryExpressionTree) right; + + assertTree(unaryLeft.operand()).isUnaryExpression(UnaryExpressionTree.Operator.NEGATE); + assertTree(unaryRight.operand()).isInstanceOf(ParenthesizedExpressionTree.class); + ParenthesizedExpressionTree parenthesizedExpression = (ParenthesizedExpressionTree) unaryRight.operand(); + assertTree(parenthesizedExpression.expression()).isBinaryExpression(Operator.CONDITIONAL_AND); + } + + @Test + void other_unary_expression() { + assertTree(parseUnary("+ 2;")).isUnaryExpression(UnaryExpressionTree.Operator.PLUS); + assertTree(parseUnary("+ +2;")).isUnaryExpression(UnaryExpressionTree.Operator.PLUS); + assertTree(parseUnary("- 2;")).isUnaryExpression(UnaryExpressionTree.Operator.MINUS); + assertTree(parseUnary("++2;")).isUnaryExpression(UnaryExpressionTree.Operator.INCREMENT); + assertTree(parseUnary("-- 2;")).isUnaryExpression(UnaryExpressionTree.Operator.DECREMENT); + } + + @Test + void parenthesized_expression() { + BinaryExpressionTree binary = parseBinary("((a && b) && (c || d)) || (y\n|| z);"); + assertTree(binary.leftOperand()).isInstanceOf(ParenthesizedExpressionTree.class); + assertTree(binary.rightOperand()).isInstanceOf(ParenthesizedExpressionTree.class); + ParenthesizedExpressionTree left = (ParenthesizedExpressionTree) binary.leftOperand(); + ParenthesizedExpressionTree right = (ParenthesizedExpressionTree) binary.rightOperand(); + + assertTree(left).hasTextRange(1, 0, 1, 22); + assertTree(left.expression()).isBinaryExpression(Operator.CONDITIONAL_AND); + BinaryExpressionTree innerBinary = (BinaryExpressionTree) left.expression(); + assertTree(innerBinary.leftOperand()).isInstanceOf(ParenthesizedExpressionTree.class); + assertTree(innerBinary.rightOperand()).isInstanceOf(ParenthesizedExpressionTree.class); + assertTree(right).hasTextRange(1, 26, 2, 5); + assertTree(right.expression()).isBinaryExpression(Operator.CONDITIONAL_OR); + } + + @Test + void conditional_and_with_multiple_operands() { + BinaryExpressionTree binary = parseBinary("x && y && z;"); + assertTree(binary).isBinaryExpression(Operator.CONDITIONAL_AND); + assertTree(binary.leftOperand()).isIdentifier("x"); + assertTree(binary.rightOperand()).isBinaryExpression(Operator.CONDITIONAL_AND); + assertRange(binary.operatorToken().textRange()).hasRange(1, 2, 1, 4); + } + + @Test + void additive_expression_with_multiple_operands() { + BinaryExpressionTree binary = parseBinary("x + y\n- z;"); + assertTree(binary).isBinaryExpression(Operator.PLUS); + assertTree(binary.leftOperand()).isIdentifier("x"); + assertTree(binary.rightOperand()).isBinaryExpression(Operator.MINUS).hasTextRange(1, 4, 2, 3); + } + + @Test + void binary_expression_with_place_holder() { + BinaryExpressionTree binary = parseBinary("_ && y;"); + assertTree(binary).isBinaryExpression(Operator.CONDITIONAL_AND); + assertTree(binary.leftOperand()).isInstanceOf(PlaceHolderTree.class); + assertTree(binary.rightOperand()).isIdentifier("y"); + } + + @Test + void variable_declaration() { + Tree tree = converter.parse("int var x;").children().get(0); + Tree valueTree = converter.parse("int val x;").children().get(0); + Tree anotherTree = converter.parse("int var x;").children().get(0); + Tree yetAnotherTree = converter.parse("boolean var x;").children().get(0); + assertThat(tree).isInstanceOf(VariableDeclarationTree.class); + + VariableDeclarationTree varDeclX = (VariableDeclarationTree) tree; + + assertThat(varDeclX.children()).hasSize(2); + assertTree(varDeclX.type()).isIdentifier("int"); + assertTree(varDeclX.identifier()).isIdentifier("x"); + assertTree(varDeclX).isEquivalentTo(anotherTree); + assertTree(varDeclX).isNotEquivalentTo(yetAnotherTree); + assertTree(varDeclX).isNotEquivalentTo(valueTree); + } + + @Test + void variable_declaration_annotated() { + Tree tree = converter.parse("@MyAnnotation\n" + + "int val x;").children().get(0); + List annotations = tree.metaData().annotations(); + assertThat(annotations).hasSize(1); + Annotation annotation = annotations.get(0); + assertThat(annotation.shortName()).isEqualTo("MyAnnotation"); + assertThat(annotation.argumentsText()).isEmpty(); + } + + @Test + void variable_declaration_with_initializer() { + Tree tree = converter.parse("int var x = 0;").children().get(0); + Tree anotherTree = converter.parse("int val x = 0;").children().get(0); + assertThat(tree).isInstanceOf(VariableDeclarationTree.class); + assertThat(anotherTree).isInstanceOf(VariableDeclarationTree.class); + + VariableDeclarationTree varDeclX = (VariableDeclarationTree) tree; + VariableDeclarationTree valDeclX = (VariableDeclarationTree) anotherTree; + + assertThat(varDeclX.children()).hasSize(3); + assertTree(varDeclX.type()).isIdentifier("int"); + assertTree(varDeclX.identifier()).isIdentifier("x"); + assertTree(varDeclX.initializer()).isLiteral("0"); + assertThat(varDeclX.isVal()).isFalse(); + assertThat(valDeclX.children()).hasSize(3); + assertTree(valDeclX.type()).isIdentifier("int"); + assertTree(valDeclX.identifier()).isIdentifier("x"); + assertTree(valDeclX.initializer()).isLiteral("0"); + assertThat(valDeclX.isVal()).isTrue(); + assertTree(varDeclX).isEquivalentTo(converter.parse("int var x = 0;").children().get(0)); + assertTree(varDeclX).isNotEquivalentTo(valDeclX); + assertTree(varDeclX).isNotEquivalentTo(converter.parse("myint var x = 0;").children().get(0)); + assertTree(varDeclX).isNotEquivalentTo(converter.parse("int var x = 1;").children().get(0)); + assertTree(varDeclX).isNotEquivalentTo(converter.parse("var x = 0;").children().get(0)); + assertTree(varDeclX).isNotEquivalentTo(converter.parse("var x;").children().get(0)); + } + + @Test + void class_with_identifier_and_body() { + ClassDeclarationTree classe = parseClass("class MyClass { int val x; fun foo (x); } "); + assertThat(classe.children()).hasSize(1); + assertThat(classe.classTree()).isInstanceOf(NativeTree.class); + assertTree(classe.identifier()).isIdentifier("MyClass"); + NativeTree classChildren = (NativeTree) classe.classTree(); + assertThat(classChildren.children()).hasSize(3); + assertTree(classChildren.children().get(0)).isIdentifier("MyClass"); + assertThat(classChildren.children().get(1)).isInstanceOf(VariableDeclarationTree.class); + assertTree(classChildren.children().get(2)).isInstanceOf(FunctionDeclarationTree.class); + } + + @Test + void class_with_constructor() { + ClassDeclarationTree classe = parseClass("class MyClass { fun constructor(x) { } fun foo(x) { } } "); + assertThat(classe.children()).hasSize(1); + assertThat(classe.classTree()).isInstanceOf(NativeTree.class); + assertTree(classe.identifier()).isIdentifier("MyClass"); + NativeTree classChildren = (NativeTree) classe.classTree(); + assertThat(classChildren.children()).hasSize(3); + assertTree(classChildren.children().get(0)).isIdentifier("MyClass"); + assertTree(classChildren.children().get(1)).isInstanceOf(FunctionDeclarationTree.class); + assertTree(classChildren.children().get(2)).isInstanceOf(FunctionDeclarationTree.class); + FunctionDeclarationTree constructorTree = (FunctionDeclarationTree) classChildren.children().get(1); + assertThat(constructorTree.isConstructor()).isTrue(); + FunctionDeclarationTree functionTree = (FunctionDeclarationTree) classChildren.children().get(2); + assertThat(functionTree.isConstructor()).isFalse(); + } + + @Test + void class_without_body() { + ClassDeclarationTree classe = parseClass("class MyClass { } "); + assertThat(classe.children()).hasSize(1); + assertThat(classe.classTree()).isInstanceOf(NativeTree.class); + assertTree(classe.identifier()).isIdentifier("MyClass"); + assertRange(classe.identifier().textRange()).hasRange(1, 6, 1, 13); + NativeTree classChildren = (NativeTree) classe.classTree(); + assertThat(classChildren.children()).hasSize(1); + assertTree(classChildren.children().get(0)).isIdentifier("MyClass"); + } + + @Test + void class_without_identifier() { + ClassDeclarationTree classe = parseClass("class { int val x; } "); + assertThat(classe.children()).hasSize(1); + assertThat(classe.classTree()).isInstanceOf(NativeTree.class); + assertTree(classe.identifier()).isNull(); + NativeTree classChildren = (NativeTree) classe.classTree(); + assertThat(classChildren.children()).hasSize(1); + assertThat(classChildren.children().get(0)).isInstanceOf(VariableDeclarationTree.class); + } + + @Test + void class_without_identifier_and_body() { + ClassDeclarationTree classe = parseClass("class { } "); + assertThat(classe.children()).hasSize(1); + assertThat(classe.classTree()).isInstanceOf(NativeTree.class); + assertTree(classe.identifier()).isNull(); + NativeTree classChildren = (NativeTree) classe.classTree(); + assertThat(classChildren.children()).isEmpty(); + } + + @Test + void nested_class_in_function() { + FunctionDeclarationTree func = parseFunction("fun foo() { class { } }"); + assertThat(func.children()).hasSize(2); + assertTree(func.name()).isIdentifier("foo"); + assertTree(func.body()).isBlock(ClassDeclarationTree.class); + } + + @Test + void class_with_annotation() { + ClassDeclarationTree classe = parseClass("@MyAnnotation(\"abc\") class { } "); + List annotations = classe.metaData().annotations(); + assertThat(annotations).hasSize(1); + Annotation annotation = annotations.get(0); + assertThat(annotation.shortName()).isEqualTo("MyAnnotation"); + assertThat(annotation.argumentsText()).hasSize(1).containsExactly("\"abc\""); + } + + @Test + void class_with_annotation_complex_arguments() { + ClassDeclarationTree classe = parseClass("@MyAnnotation(\"abc\", value =\"b\", value={\"b\",\"c\"}) class { } "); + List annotations = classe.metaData().annotations(); + assertThat(annotations).hasSize(1); + Annotation annotation = annotations.get(0); + assertThat(annotation.shortName()).isEqualTo("MyAnnotation"); + assertThat(annotation.argumentsText()).hasSize(3).containsExactly("\"abc\"", "value=\"b\"", "value={\"b\",\"c\"}"); + } + + @Test + void class_and_method_with_annotation() { + ClassDeclarationTree classe = parseClass("@MyAnnotation1 @MyAnnotation2(\"abc\") class { @MyFunAnnotation(\"cba\") fun foo() { } } "); + List annotations = classe.metaData().annotations(); + assertThat(annotations).hasSize(2); + Annotation annotation1 = annotations.get(0); + assertThat(annotation1.shortName()).isEqualTo("MyAnnotation1"); + assertThat(annotation1.argumentsText()).isEmpty(); + Annotation annotation2 = annotations.get(1); + assertThat(annotation2.shortName()).isEqualTo("MyAnnotation2"); + assertThat(annotation2.argumentsText()).containsExactly("\"abc\""); + + FunctionDeclarationTree nestedFunction = (FunctionDeclarationTree) classe.children().get(0).children().get(0); + List functionAnnotations = nestedFunction.metaData().annotations(); + assertThat(functionAnnotations).hasSize(1); + Annotation myFunAnnotation = functionAnnotations.get(0); + assertThat(myFunAnnotation.shortName()).isEqualTo("MyFunAnnotation"); + assertThat(myFunAnnotation.argumentsText()).containsExactly("\"cba\""); + } + + @Test + void function() { + FunctionDeclarationTree function = parseFunction("private int fun foo(x1, x2) { x1 + x2 }"); + assertThat(function.name().name()).isEqualTo("foo"); + assertThat(function.modifiers()).hasSize(1); + assertTree(function.returnType()).isIdentifier("int"); + assertThat(function.formalParameters()).hasSize(2); + assertTree(function.formalParameters().get(0)).hasParameterName("x1"); + assertThat(function.body()).isNotNull(); + assertThat(function.nativeChildren()).isEmpty(); + + FunctionDeclarationTree publicFunction = parseFunction("public int fun foo(p1);"); + assertThat(publicFunction.formalParameters()).hasSize(1); + assertTree(publicFunction.formalParameters().get(0)).hasParameterName("p1"); + + FunctionDeclarationTree emptyParamFunction = parseFunction("private int fun foo();"); + assertThat(emptyParamFunction.formalParameters()).isEmpty(); + assertThat(emptyParamFunction.body()).isNull(); + + Tree privateModifier1 = function.modifiers().get(0); + Tree publicModifier1 = publicFunction.modifiers().get(0); + Tree privateModifier2 = emptyParamFunction.modifiers().get(0); + assertTree(privateModifier1).isNotEquivalentTo(publicModifier1); + assertTree(privateModifier1).isEquivalentTo(privateModifier2); + assertTree(privateModifier1).isEquivalentTo(new ModifierTreeImpl(null, PRIVATE)); + assertTree(publicModifier1).isEquivalentTo(new ModifierTreeImpl(null, PUBLIC)); + + FunctionDeclarationTree simpleFunction = parseFunction("fun foo() {}"); + assertThat(simpleFunction.modifiers()).isEmpty(); + assertThat(simpleFunction.returnType()).isNull(); + assertThat(simpleFunction.body().statementOrExpressions()).isEmpty(); + + FunctionDeclarationTree overriddenFunction = parseFunction("override int fun foo();"); + assertThat(overriddenFunction.modifiers()).hasSize(1); + ModifierTree modifier = (ModifierTree) overriddenFunction.modifiers().get(0); + assertThat(modifier.kind()).isEqualTo(OVERRIDE); + + FunctionDeclarationTree functWithNativeModifier = parseFunction("native [] {} int fun foo();"); + assertThat(functWithNativeModifier.modifiers()).hasSize(1); + assertThat(functWithNativeModifier.modifiers().get(0)).isInstanceOf(NativeTree.class); + + FunctionDeclarationTree noNameFunction = parseFunction("fun() {}"); + assertThat(noNameFunction.name()).isNull(); + + FunctionDeclarationTree functionWithDefaultParam = parseFunction("fun foo(p1 = 1, p2, p3 = 1 + 3) {}"); + Tree p1 = functionWithDefaultParam.formalParameters().get(0); + Tree p2 = functionWithDefaultParam.formalParameters().get(1); + Tree p3 = functionWithDefaultParam.formalParameters().get(2); + assertTree(p1).hasParameterName("p1"); + assertTree(p2).hasParameterName("p2"); + assertTree(p3).hasParameterName("p3"); + assertTree(((ParameterTree) p1).defaultValue()).isLiteral("1"); + assertTree(((ParameterTree) p2).defaultValue()).isNull(); + assertTree(((ParameterTree) p3).defaultValue()).isBinaryExpression(Operator.PLUS); + + FunctionDeclarationTree functionWithModifier = parseFunction("fun foo(p1, native [] {} p2) {}"); + assertTree(functionWithModifier).hasParameterNames("p1", "p2"); + Tree p1Mod = functionWithModifier.formalParameters().get(0); + Tree p2Mod = functionWithModifier.formalParameters().get(1); + assertThat(((ParameterTree) p1Mod).modifiers()).isEmpty(); + assertThat(((ParameterTree) p2Mod).modifiers()).hasSize(1); + assertThat(((ParameterTree) p2Mod).modifiers().get(0)).isInstanceOf(NativeTree.class); + } + + @Test + void function_with_local_annotations() { + FunctionDeclarationTree function = parseFunction("private int fun foo() {\n" + + "@MyAnnotation\n" + + "val i = 1;\n" + + "}"); + List localVariableAnnotations = function.body().statementOrExpressions().get(0).metaData().annotations(); + assertThat(localVariableAnnotations).hasSize(1); + Annotation annotation = localVariableAnnotations.get(0); + assertThat(annotation.shortName()).isEqualTo("MyAnnotation"); + assertThat(annotation.argumentsText()).isEmpty(); + } + + @Test + void function_with_annotated_parameters() { + FunctionDeclarationTree function = parseFunction("private int fun foo(int a, @B int b, @C int c, int d) {}"); + List parameters = function.formalParameters(); + + List bAnnotations = parameters.get(1).metaData().annotations(); + List cAnnotations = parameters.get(2).metaData().annotations(); + + assertThat(parameters.get(0).metaData().annotations()).isEmpty(); + assertThat(bAnnotations).hasSize(1); + assertThat(cAnnotations).hasSize(1); + assertThat(parameters.get(3).metaData().annotations()).isEmpty(); + + Annotation bAnnotation = bAnnotations.get(0); + assertThat(bAnnotation.shortName()).isEqualTo("B"); + assertThat(bAnnotation.argumentsText()).isEmpty(); + Annotation cAnnotation = cAnnotations.get(0); + assertThat(cAnnotation.shortName()).isEqualTo("C"); + assertThat(cAnnotation.argumentsText()).isEmpty(); + } + + @Test + void if_without_else() { + Tree tree = converter.parse("if (x > 0) { x = 1; };").children().get(0); + assertThat(tree).isInstanceOf(IfTree.class); + IfTree ifTree = (IfTree) tree; + assertTree(ifTree).hasTextRange(1, 0, 1, 21); + assertTree(ifTree.condition()).isBinaryExpression(GREATER_THAN); + assertThat(ifTree.elseBranch()).isNull(); + assertThat(ifTree.ifKeyword().text()).isEqualTo("if"); + assertThat(ifTree.elseKeyword()).isNull(); + } + + @Test + void if_with_else() { + Tree tree = converter.parse("if (x > 0) { x == 1; } else { y };").children().get(0); + assertThat(tree).isInstanceOf(IfTree.class); + IfTree ifTree = (IfTree) tree; + assertTree(ifTree).hasTextRange(1, 0, 1, 33); + assertTree(ifTree.condition()).isBinaryExpression(GREATER_THAN); + assertTree(ifTree.thenBranch()).isBlock(BinaryExpressionTree.class).hasTextRange(1, 11, 1, 22); + assertTree(ifTree.elseBranch()).isBlock(IdentifierTree.class); + assertThat(ifTree.ifKeyword().text()).isEqualTo("if"); + assertThat(ifTree.elseKeyword().text()).isEqualTo("else"); + } + + @Test + void if_with_else_if() { + Tree tree = converter.parse("if (x > 0) { x == 1; } else if (x < 1) { y };").children().get(0); + assertThat(tree).isInstanceOf(IfTree.class); + IfTree ifTree = (IfTree) tree; + assertTree(ifTree.elseBranch()).isInstanceOf(IfTree.class); + } + + @Test + void match() { + Tree tree = converter.parse("match(x) { 1 -> a; else -> b; };").children().get(0); + assertTree(tree).isInstanceOf(MatchTree.class).hasTextRange(1, 0, 1, 31); + MatchTree matchTree = (MatchTree) tree; + assertTree(matchTree.expression()).isIdentifier("x"); + assertThat(matchTree.cases()).hasSize(2); + assertTree(matchTree.cases().get(0).expression()).isLiteral("1"); + assertTree(matchTree.cases().get(1).expression()).isNull(); + assertTree(matchTree.cases().get(1)).hasTextRange(1, 19, 1, 29); + assertThat(matchTree.keyword().text()).isEqualTo("match"); + } + + @Test + void match_without_expression() { + Tree tree = converter.parse("match() { 1 -> a; else -> b; };").children().get(0); + MatchTree matchTree = (MatchTree) tree; + assertTree(matchTree.expression()).isNull(); + } + + @Test + void match_case_without_body() { + Tree tree = converter.parse("match(x) { 1 -> ; 2 -> a; else ->; };").children().get(0); + MatchTree matchTree = (MatchTree) tree; + assertTree(matchTree.cases().get(0).body()).isNull(); + assertTree(matchTree.cases().get(1).body()).isNotNull(); + assertTree(matchTree.cases().get(2).body()).isNull(); + } + + @Test + void for_loop() { + Tree tree = converter.parse("for (var x = list) { x; };").children().get(0); + assertTree(tree).isInstanceOf(LoopTree.class).hasTextRange(1, 0, 1, 25); + LoopTree forLoop = (LoopTree) tree; + assertThat(forLoop.condition()).isNotNull(); + assertThat(forLoop.condition().children()).hasSize(2); + assertTree(forLoop.body()).isBlock(IdentifierTree.class); + assertThat(forLoop.kind()).isEqualTo(FOR); + assertThat(forLoop.keyword().text()).isEqualTo("for"); + } + + @Test + void for_loop_without_condition() { + Tree tree = converter.parse("for {};").children().get(0); + assertTree(tree).isInstanceOf(LoopTree.class).hasTextRange(1, 0, 1, 6); + LoopTree forLoop = (LoopTree) tree; + assertThat(forLoop.condition()).isNull(); + assertTree(forLoop.body()).isBlock(); + assertThat(forLoop.body().children()).isEmpty(); + } + + @Test + void while_loop() { + Tree tree = converter.parse("while (x > y) { x = x-1; };").children().get(0); + assertTree(tree).isInstanceOf(LoopTree.class).hasTextRange(1, 0, 1, 26); + LoopTree forLoop = (LoopTree) tree; + assertTree(forLoop.condition()).isBinaryExpression(GREATER_THAN); + assertTree(forLoop.body()).isBlock(AssignmentExpressionTree.class); + assertThat(forLoop.kind()).isEqualTo(WHILE); + assertThat(forLoop.keyword().text()).isEqualTo("while"); + } + + @Test + void doWhile_loop() { + Tree tree = converter.parse("do { x = x-1; } while (x > y);").children().get(0); + assertTree(tree).isInstanceOf(LoopTree.class).hasTextRange(1, 0, 1, 29); + LoopTree forLoop = (LoopTree) tree; + assertTree(forLoop.condition()).isBinaryExpression(GREATER_THAN); + assertTree(forLoop.body()).isBlock(AssignmentExpressionTree.class); + assertThat(forLoop.kind()).isEqualTo(DOWHILE); + assertThat(forLoop.keyword().text()).isEqualTo("do"); + } + + @Test + void try_catch_finally() { + Tree tree = converter.parse("try { 1 } catch (e) {} catch () {} finally {};").children().get(0); + assertTree(tree).isInstanceOf(ExceptionHandlingTree.class).hasTextRange(1, 0, 1, 45); + ExceptionHandlingTree exceptionHandlingTree = (ExceptionHandlingTree) tree; + assertTree(exceptionHandlingTree.tryBlock()).isBlock(LiteralTree.class); + assertThat(exceptionHandlingTree.catchBlocks()).hasSize(2); + assertTree(exceptionHandlingTree.catchBlocks().get(0).catchParameter()).hasParameterName("e"); + assertTree(exceptionHandlingTree.catchBlocks().get(0).catchBlock()).isBlock(); + assertTree(exceptionHandlingTree.catchBlocks().get(1).catchParameter()).isNull(); + assertTree(exceptionHandlingTree.finallyBlock()).isBlock(); + } + + @Test + void try_catch() { + Tree tree = converter.parse("try { 1 } catch (e) {};").children().get(0); + assertTree(tree).isInstanceOf(ExceptionHandlingTree.class).hasTextRange(1, 0, 1, 22); + ExceptionHandlingTree exceptionHandlingTree = (ExceptionHandlingTree) tree; + assertTree(exceptionHandlingTree.tryBlock()).isBlock(LiteralTree.class); + assertThat(exceptionHandlingTree.catchBlocks()).hasSize(1); + assertTree(exceptionHandlingTree.catchBlocks().get(0).catchParameter()).hasParameterName("e"); + assertTree(exceptionHandlingTree.catchBlocks().get(0).catchBlock()).isBlock(); + assertThat(exceptionHandlingTree.catchBlocks().get(0).keyword().text()).isEqualTo("catch"); + assertTree(exceptionHandlingTree.finallyBlock()).isNull(); + } + + @Test + void try_finally() { + Tree tree = converter.parse("try { 1 } finally {};").children().get(0); + assertTree(tree).isInstanceOf(ExceptionHandlingTree.class).hasTextRange(1, 0, 1, 20); + ExceptionHandlingTree exceptionHandlingTree = (ExceptionHandlingTree) tree; + assertTree(exceptionHandlingTree.tryBlock()).isBlock(LiteralTree.class); + assertThat(exceptionHandlingTree.catchBlocks()).isEmpty(); + assertTree(exceptionHandlingTree.finallyBlock()).isBlock(); + } + + @Test + void natives() { + Tree tree = converter.parse("native [] {};").children().get(0); + assertTree(tree).isInstanceOf(NativeTree.class).hasTextRange(1, 0, 1, 12); + + tree = converter.parse("native [] { [x] } = x;").children().get(0); + assertTree(tree).isAssignmentExpression(AssignmentExpressionTree.Operator.EQUAL); + AssignmentExpressionTree assignment = (AssignmentExpressionTree) tree; + assertTree(assignment.leftHandSide()).isInstanceOf(NativeTree.class).hasTextRange(1, 0, 1, 17); + } + + @Test + void simple_assignment() { + Tree tree = converter.parse("x = 1;").children().get(0); + assertTree(tree).isAssignmentExpression(AssignmentExpressionTree.Operator.EQUAL).hasTextRange(1, 0, 1, 5); + } + + @Test + void nested_assignments() { + Tree tree = converter.parse("x += y += 2;").children().get(0); + assertTree(tree).isAssignmentExpression(AssignmentExpressionTree.Operator.PLUS_EQUAL).hasTextRange(1, 0, 1, 11); + AssignmentExpressionTree assignment = (AssignmentExpressionTree) tree; + assertTree(assignment.leftHandSide()).isIdentifier("x"); + assertTree(assignment.statementOrExpression()).isAssignmentExpression(AssignmentExpressionTree.Operator.PLUS_EQUAL).hasTextRange(1, 5, 1, 11); + } + + @Test + void top_level_tree() { + Tree tree1 = converter.parse("int fun foo(p1);\nx == 3;"); + Tree tree2 = converter.parse("x + y\n\n- z;"); + Tree emptyTree = converter.parse(""); + assertTree(tree1) + .isInstanceOf(TopLevelTree.class) + .hasChildren(FunctionDeclarationTree.class, BinaryExpressionTree.class) + .hasTextRange(1, 0, 2, 7); + assertTree(tree2) + .isInstanceOf(TopLevelTree.class) + .hasChildren(BinaryExpressionTree.class) + .hasTextRange(1, 0, 3, 4); + assertTree(emptyTree) + .isInstanceOf(TopLevelTree.class) + .hasChildren() + .hasTextRange(1, 0, 1, 0); + } + + @Test + void comments() { + BinaryExpressionTree binary = parseBinary("/* comment1 */ x /* comment2 */ == // comment3\n1;"); + List comments = binary.metaData().commentsInside(); + assertThat(comments).hasSize(2); + Comment comment = comments.get(0); + assertRange(comment.textRange()).hasRange(1, 17, 1, 31); + assertRange(comment.contentRange()).hasRange(1, 19, 1, 29); + assertThat(comment.text()).isEqualTo("/* comment2 */"); + assertThat(comment.contentText()).isEqualTo(" comment2 "); + assertThat(comments.get(1).contentText()).isEqualTo(" comment3"); + assertRange(comments.get(1).contentRange()).hasRange(1, 37, 1, 46); + } + + @Test + void decimalLiterals() { + Tree tree = converter.parse("0; 5; 10; 123; 1010; 5554; 12345567;"); + String[] values = {"0", "5", "10", "123", "1010", "5554", "12345567"}; + + assertTree(tree).isNotNull(); + assertTree(tree).isInstanceOf(TopLevelTree.class); + TopLevelTree topLevelTree = (TopLevelTree) tree; + assertThat(topLevelTree.declarations()).hasSize(7); + + for (int i = 0; i < topLevelTree.declarations().size(); i++) { + assertTree(topLevelTree.declarations().get(i)).isLiteral(values[i]); + } + } + + @Test + void stringLiterals() { + List values = Arrays.asList("\"a\"", "\"string\"", "\"string with spaces\""); + List content = Arrays.asList("a", "string", "string with spaces"); + + String slangCode = values.stream().collect(Collectors.joining(";")); + Tree tree = converter.parse(slangCode + ";"); + + assertTree(tree).isNotNull(); + assertTree(tree).isInstanceOf(TopLevelTree.class); + TopLevelTree topLevelTree = (TopLevelTree) tree; + assertThat(topLevelTree.declarations()).hasSize(3); + + for (int i = 0; i < topLevelTree.declarations().size(); i++) { + assertTree(topLevelTree.declarations().get(i)).isLiteral(values.get(i)); + assertTree(topLevelTree.declarations().get(i)).isStringLiteral(content.get(i)); + } + } + + @Test + void jump() { + JumpTree jumpTree = (JumpTree) converter.parse("break foo;").children().get(0); + assertThat(jumpTree.label().name()).isEqualTo("foo"); + assertThat(jumpTree.kind()).isEqualTo(JumpTree.JumpKind.BREAK); + + jumpTree = (JumpTree) converter.parse("break;").children().get(0); + assertThat(jumpTree.label()).isNull(); + assertThat(jumpTree.kind()).isEqualTo(JumpTree.JumpKind.BREAK); + + jumpTree = (JumpTree) converter.parse("continue;").children().get(0); + assertThat(jumpTree.label()).isNull(); + assertThat(jumpTree.kind()).isEqualTo(JumpTree.JumpKind.CONTINUE); + + jumpTree = (JumpTree) converter.parse("continue foo;").children().get(0); + assertThat(jumpTree.label().name()).isEqualTo("foo"); + assertThat(jumpTree.kind()).isEqualTo(JumpTree.JumpKind.CONTINUE); + } + + @Test + void returnTree() { + ReturnTree returnTree = (ReturnTree) converter.parse("return true;").children().get(0); + assertThat(returnTree.body()).isInstanceOf(LiteralTree.class); + + returnTree = (ReturnTree) converter.parse("return;").children().get(0); + assertThat(returnTree.body()).isNull(); + } + + @Test + void tokens() { + Tree topLevel = converter.parse("if (cond == 42) \"a\";"); + IfTree ifTree = (IfTree) topLevel.children().get(0); + assertThat(topLevel.metaData().tokens()).extracting(Token::text) + .containsExactly("if", "(", "cond", "==", "42", ")", "\"a\"", ";"); + assertThat(topLevel.metaData().tokens()).extracting(Token::type) + .containsExactly(KEYWORD, OTHER, OTHER, OTHER, OTHER, OTHER, STRING_LITERAL, OTHER); + assertRange(topLevel.metaData().tokens().get(1).textRange()).hasRange(1, 3, 1, 4); + assertThat(ifTree.condition().metaData().tokens()).extracting(Token::text).containsExactly("cond", "==", "42"); + } + + @Test + void methodInvocations() { + Tree topLevelNoArgument = converter.parse("function();"); + assertTree(topLevelNoArgument.children().get(0)).isInstanceOf(FunctionInvocationTree.class); + FunctionInvocationTree functionInvocationNoArgument = (FunctionInvocationTree) topLevelNoArgument.children().get(0); + assertTree(functionInvocationNoArgument.memberSelect()).isIdentifier("function"); + assertThat(functionInvocationNoArgument.arguments()).isEmpty(); + + Tree topLevelTwoArgs = converter.parse("function(1, 2);"); + assertTree(topLevelTwoArgs.children().get(0)).isInstanceOf(FunctionInvocationTree.class); + FunctionInvocationTree functionInvocationTreeTwoArgs = (FunctionInvocationTree) topLevelTwoArgs.children().get(0); + assertTree(functionInvocationTreeTwoArgs.memberSelect()).isIdentifier("function"); + assertThat(functionInvocationTreeTwoArgs.arguments()).hasSize(2); + assertThat(functionInvocationTreeTwoArgs.descendants() + .anyMatch(e -> e instanceof LiteralTree && ((LiteralTree) e).value().equals("1"))).isTrue(); + + assertTree(topLevelNoArgument).isEquivalentTo(topLevelNoArgument); + assertTree(topLevelNoArgument).isEquivalentTo(converter.parse("function();")); + assertTree(topLevelNoArgument).isNotEquivalentTo(converter.parse("function2();")); + assertTree(topLevelNoArgument).isNotEquivalentTo(converter.parse("function(1);")); + assertTree(topLevelNoArgument).isNotEquivalentTo(topLevelTwoArgs); + assertTree(converter.parse("function(1);")).isEquivalentTo(converter.parse("function(1);")); + assertTree(converter.parse("function(1);")).isNotEquivalentTo(topLevelTwoArgs); + + assertThat(functionInvocationNoArgument.descendants() + .anyMatch(e -> e instanceof IdentifierTree && ((IdentifierTree) e).name().equals("function"))).isTrue(); + } + + @Test + void memberSelect() { + Tree topLevelNoArgument = converter.parse("A.B;"); + assertTree(topLevelNoArgument.children().get(0)).isInstanceOf(MemberSelectTree.class); + + MemberSelectTree memberSelectTree = (MemberSelectTree) topLevelNoArgument.children().get(0); + assertTree(memberSelectTree.identifier()).isIdentifier("B"); + assertTree(memberSelectTree.expression()).isIdentifier("A"); + } + + @Test + void memberSelectInAssignment() { + Tree topLevelNoArgument = converter.parse("A.B.F = 1;"); + assertTree(topLevelNoArgument.children().get(0)).isInstanceOf(AssignmentExpressionTree.class); + AssignmentExpressionTree assignmentExpressionTree = (AssignmentExpressionTree) topLevelNoArgument.children().get(0); + assertTree(assignmentExpressionTree.leftHandSide()).isInstanceOf(MemberSelectTree.class); + + MemberSelectTree memberSelectTree = (MemberSelectTree)assignmentExpressionTree.leftHandSide(); + assertTree(memberSelectTree.identifier()).isIdentifier("F"); + + assertTree(memberSelectTree.expression()).isInstanceOf(MemberSelectTree.class); + MemberSelectTree nestedMemberSelect = (MemberSelectTree)memberSelectTree.expression(); + assertTree(nestedMemberSelect.identifier()).isIdentifier("B"); + assertTree(nestedMemberSelect.expression()).isIdentifier("A"); + } + + @Test + void memberSelectInFunctionCall() { + Tree topLevelNoArgument = converter.parse("A.B.F();"); + assertTree(topLevelNoArgument.children().get(0)).isInstanceOf(FunctionInvocationTree.class); + FunctionInvocationTree functionInvocationNoArgument = (FunctionInvocationTree) topLevelNoArgument.children().get(0); + assertTree(functionInvocationNoArgument.memberSelect()).isInstanceOf(MemberSelectTree.class); + + MemberSelectTree memberSelectTree = (MemberSelectTree)functionInvocationNoArgument.memberSelect(); + assertTree(memberSelectTree.identifier()).isIdentifier("F"); + + assertTree(memberSelectTree.expression()).isInstanceOf(MemberSelectTree.class); + MemberSelectTree nestedMemberSelect = (MemberSelectTree)memberSelectTree.expression(); + assertTree(nestedMemberSelect.identifier()).isIdentifier("B"); + assertTree(nestedMemberSelect.expression()).isIdentifier("A"); + } + + @Test + void integerLiterals() { + Tree tree = converter.parse("0252; 0o252; 0O252; 170; 0xaa; 0B10;"); + IntegerLiteralTree literal0 = (IntegerLiteralTree) tree.children().get(0); + assertTree(literal0).isLiteral("0252"); + assertThat(literal0.getBase()).isEqualTo(IntegerLiteralTree.Base.OCTAL); + assertThat(literal0.getIntegerValue().intValue()).isEqualTo(170); + IntegerLiteralTree literal1 = (IntegerLiteralTree) tree.children().get(1); + assertTree(literal1).isLiteral("0o252"); + assertThat(literal1.getBase()).isEqualTo(IntegerLiteralTree.Base.OCTAL); + assertThat(literal1.getIntegerValue().intValue()).isEqualTo(170); + IntegerLiteralTree literal2 = (IntegerLiteralTree) tree.children().get(2); + assertTree(literal2).isLiteral("0O252"); + assertThat(literal2.getBase()).isEqualTo(IntegerLiteralTree.Base.OCTAL); + assertThat(literal2.getIntegerValue().intValue()).isEqualTo(170); + IntegerLiteralTree literal3 = (IntegerLiteralTree) tree.children().get(3); + assertTree(literal3).isLiteral("170"); + assertThat(literal3.getBase()).isEqualTo(IntegerLiteralTree.Base.DECIMAL); + assertThat(literal3.getIntegerValue().intValue()).isEqualTo(170); + IntegerLiteralTree literal4 = (IntegerLiteralTree) tree.children().get(4); + assertTree(literal4).isLiteral("0xaa"); + assertThat(literal4.getBase()).isEqualTo(IntegerLiteralTree.Base.HEXADECIMAL); + assertThat(literal4.getIntegerValue().intValue()).isEqualTo(170); + IntegerLiteralTree literal5 = (IntegerLiteralTree) tree.children().get(5); + assertTree(literal5).isLiteral("0B10"); + assertThat(literal5.getBase()).isEqualTo(IntegerLiteralTree.Base.BINARY); + assertThat(literal5.getIntegerValue().intValue()).isEqualTo(2); + } + + @Test + void placeholderAssignment() { + Tree placeholderAssignment = converter.parse("A._=\"xxx\";"); + assertTree(placeholderAssignment.children().get(0)).isInstanceOf(AssignmentExpressionTree.class); + AssignmentExpressionTree assignment = (AssignmentExpressionTree) placeholderAssignment.children().get(0); + + assertTree(assignment.leftHandSide()).isInstanceOf(MemberSelectTree.class); + MemberSelectTree lhs = (MemberSelectTree) assignment.leftHandSide(); + + assertTree(lhs.expression()).isInstanceOf(IdentifierTree.class); + assertTree(lhs.identifier()).isInstanceOf(PlaceHolderTree.class); + } + + @Test + void parse_failure_1() { + ParseException e = assertThrows(ParseException.class, + () -> converter.parse("x + 1")); + assertThat(e).hasMessage("missing ';' before '' at position 1:5"); + } + + @Test + void parse_failure_2() { + ParseException e = assertThrows(ParseException.class, + () -> converter.parse("private fun fun foo() {}")); + assertThat(e).hasMessage("Unexpected parsing error occurred. Last found valid token: 'private' at position 1:0"); + } + + @Test + void unsupportedIdentifier() { + Tree topLevelTree = converter.parse("A.__;"); + assertTree(topLevelTree.children().get(0)).isInstanceOf(MemberSelectTree.class); + MemberSelectTree memberSelectTree = (MemberSelectTree) topLevelTree.children().get(0); + IdentifierTree identifierTree = memberSelectTree.identifier(); + assertTree(identifierTree).isNull(); + } + + private BinaryExpressionTree parseBinary(String code) { + return (BinaryExpressionTree) parseExpressionOrStatement(code); + } + + private UnaryExpressionTree parseUnary(String code) { + return (UnaryExpressionTree) parseExpressionOrStatement(code); + } + + private Tree parseExpressionOrStatement(String code) { + Tree tree = converter.parse(code); + return tree.children().get(0); + } + + private FunctionDeclarationTree parseFunction(String code) { + return (FunctionDeclarationTree) converter.parse(code).children().get(0); + } + + private ClassDeclarationTree parseClass(String code) { + return (ClassDeclarationTree) converter.parse(code).children().get(0); + } +} diff --git a/slang-antlr/src/test/java/org/sonarsource/slang/antlr/SLangParserTest.java b/slang-antlr/src/test/java/org/sonarsource/slang/antlr/SLangParserTest.java new file mode 100644 index 00000000..5265d503 --- /dev/null +++ b/slang-antlr/src/test/java/org/sonarsource/slang/antlr/SLangParserTest.java @@ -0,0 +1,69 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.antlr; + +import org.sonarsource.slang.parser.SLangLexer; +import org.sonarsource.slang.parser.SLangParser; +import java.io.IOException; +import org.antlr.v4.runtime.CharStreams; +import org.antlr.v4.runtime.CommonTokenStream; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class SLangParserTest { + + private void testFile(String file) throws IOException { + SLangLexer lexer = new SLangLexer(CharStreams.fromFileName(file)); + CommonTokenStream tokens = new CommonTokenStream(lexer); + SLangParser parser = new SLangParser(tokens); + SLangParser.SlangFileContext context = parser.slangFile(); + assertThat(context.children).isNotNull(); + assertThat(context.children).isNotEmpty(); + } + + @Test + void testBinaryExpressionFile() throws IOException { + testFile("src/test/resources/binary.slang"); + } + + @Test + void testConditionalFile() throws IOException { + testFile("src/test/resources/conditional.slang"); + } + + @Test + void testAnnotationsFile() throws IOException { + testFile("src/test/resources/annotations.slang"); + } + + @Test + void testBinaryExpression() { + SLangLexer lexer = new SLangLexer(CharStreams.fromString("x = 1;\n//comment\ny = 2 + \"1\";")); + CommonTokenStream tokens = new CommonTokenStream(lexer); + SLangParser parser = new SLangParser(tokens); + SLangParser.SlangFileContext tree = parser.slangFile(); + + assertThat(tree.children) + .isNotNull() + .isNotEmpty(); + + } +} diff --git a/slang-antlr/src/test/resources/annotations.slang b/slang-antlr/src/test/resources/annotations.slang new file mode 100644 index 00000000..8ebb0b93 --- /dev/null +++ b/slang-antlr/src/test/resources/annotations.slang @@ -0,0 +1,25 @@ +@MyAnnotation +@MyAnnotation("Something") +@MyAnnotation(value = "Something") +@MyAnnotation(value = {"Something"}) +@MyAnnotation(value = {"Something", "else"}) +@MyAnnotation("something", "else") +@MyAnnotation("something", value = "else") +@MyAnnotation(value = "something", value = {"else", "and", "more"}) +class { + @MyFieldAnnotation + int val x; + + @MyMethodAnnotation + int fun test() { + @MyLocalVariableAnnotation + int val y; + } + + int fun test(@MyParameterAnnotation int a) {} + + int fun foo(int a, @B int b, @C int c, int d) {} + + void fun bar(@Suppress("slang:S1764") int a = (1 == 1), int b = (1 == 1)) {} + +} diff --git a/slang-antlr/src/test/resources/binary.slang b/slang-antlr/src/test/resources/binary.slang new file mode 100644 index 00000000..7af33f84 --- /dev/null +++ b/slang-antlr/src/test/resources/binary.slang @@ -0,0 +1,20 @@ +// Example method in slang +a = 2; +b = K + 1; +c = a - a; +d = "hello world"; +e = 'c' + 'h'; +true && false; +f = e; + +native ["DEFER"] { + [ a = 1; ] + [ b = 2; + c = b + 1; + ] +}; + +public int fun test() +{ + e = a / c; +} diff --git a/slang-antlr/src/test/resources/conditional.slang b/slang-antlr/src/test/resources/conditional.slang new file mode 100644 index 00000000..2b241e05 --- /dev/null +++ b/slang-antlr/src/test/resources/conditional.slang @@ -0,0 +1,21 @@ +public int fun test(int a){ + if (a > 0) { + return 0; + }; + + if (a > 1) { + if (a > 2) { + return 2; + } else { + return 3; + } + }; + + if (a > 2) { + return 2; + } else if (a > 3) { + return 3; + } else { + return 4; + }; +} \ No newline at end of file diff --git a/slang-api/build.gradle b/slang-api/build.gradle new file mode 100644 index 00000000..67c5fa11 --- /dev/null +++ b/slang-api/build.gradle @@ -0,0 +1,10 @@ +dependencies { + implementation 'com.google.code.findbugs:jsr305' + implementation 'com.eclipsesource.minimal-json:minimal-json' + testImplementation "org.junit.jupiter:junit-jupiter-api" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.mockito:mockito-core' + testImplementation "org.slf4j:slf4j-api" + testImplementation "ch.qos.logback:logback-classic" +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ASTConverter.java b/slang-api/src/main/java/org/sonarsource/slang/api/ASTConverter.java new file mode 100644 index 00000000..97a62e06 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ASTConverter.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.Nullable; + +public interface ASTConverter { + + /** + * Use {@link ASTConverter#parse(String, String)} instead. + * It provides improved logging when used with ASTConverterValidation. + */ + @Deprecated(since = "1.8") + Tree parse(String content); + + default Tree parse(String content, @Nullable String currentFile) { + return parse(content); + } + + default void terminate() { + // Nothing to do by default + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/Annotation.java b/slang-api/src/main/java/org/sonarsource/slang/api/Annotation.java new file mode 100644 index 00000000..6024a4ad --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/Annotation.java @@ -0,0 +1,33 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; + +public interface Annotation extends HasTextRange { + + /** + * Short name of the annotation, without qualified name. + */ + String shortName(); + + List argumentsText(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/AssignmentExpressionTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/AssignmentExpressionTree.java new file mode 100644 index 00000000..12e74ed2 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/AssignmentExpressionTree.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface AssignmentExpressionTree extends Tree { + + public enum Operator { + EQUAL, + PLUS_EQUAL, + // for other compound assignments a native tree should be created + } + + Operator operator(); + + Tree leftHandSide(); + + Tree statementOrExpression(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/BinaryExpressionTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/BinaryExpressionTree.java new file mode 100644 index 00000000..7c4002eb --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/BinaryExpressionTree.java @@ -0,0 +1,49 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface BinaryExpressionTree extends Tree { + + public enum Operator { + PLUS, + MINUS, + TIMES, + DIVIDED_BY, + + EQUAL_TO, + NOT_EQUAL_TO, + GREATER_THAN, + GREATER_THAN_OR_EQUAL_TO, + LESS_THAN, + LESS_THAN_OR_EQUAL_TO, + + CONDITIONAL_AND, + CONDITIONAL_OR, + } + + Operator operator(); + + Token operatorToken(); + + Tree leftOperand(); + + Tree rightOperand(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/BlockTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/BlockTree.java new file mode 100644 index 00000000..d4b997f6 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/BlockTree.java @@ -0,0 +1,28 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; + +public interface BlockTree extends Tree { + + List statementOrExpressions(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/CatchTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/CatchTree.java new file mode 100644 index 00000000..a5950c45 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/CatchTree.java @@ -0,0 +1,33 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface CatchTree extends Tree { + + Token keyword(); + + @CheckForNull + Tree catchParameter(); + + Tree catchBlock(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ClassDeclarationTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ClassDeclarationTree.java new file mode 100644 index 00000000..e6644267 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ClassDeclarationTree.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface ClassDeclarationTree extends Tree { + + @CheckForNull + IdentifierTree identifier(); + + Tree classTree(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/CodeVerifier.java b/slang-api/src/main/java/org/sonarsource/slang/api/CodeVerifier.java new file mode 100644 index 00000000..2ce761ee --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/CodeVerifier.java @@ -0,0 +1,24 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface CodeVerifier { + boolean containsCode(String content); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/Comment.java b/slang-api/src/main/java/org/sonarsource/slang/api/Comment.java new file mode 100644 index 00000000..3b041680 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/Comment.java @@ -0,0 +1,30 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface Comment extends HasTextRange { + + String contentText(); + + String text(); + + TextRange contentRange(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ExceptionHandlingTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ExceptionHandlingTree.java new file mode 100644 index 00000000..e2e1bd75 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ExceptionHandlingTree.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; +import java.util.List; + +public interface ExceptionHandlingTree extends Tree { + + Tree tryBlock(); + + List catchBlocks(); + + Token tryKeyword(); + + @CheckForNull + Tree finallyBlock(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/FunctionDeclarationTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/FunctionDeclarationTree.java new file mode 100644 index 00000000..e96fde00 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/FunctionDeclarationTree.java @@ -0,0 +1,46 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; +import javax.annotation.CheckForNull; + +public interface FunctionDeclarationTree extends Tree { + + List modifiers(); + + boolean isConstructor(); + + @CheckForNull + Tree returnType(); + + @CheckForNull + IdentifierTree name(); + + List formalParameters(); + + @CheckForNull + BlockTree body(); + + List nativeChildren(); + + TextRange rangeToHighlight(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/FunctionInvocationTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/FunctionInvocationTree.java new file mode 100644 index 00000000..2c01b98f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/FunctionInvocationTree.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; + +public interface FunctionInvocationTree extends Tree { + + Tree memberSelect(); + + List arguments(); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/HasKeyword.java b/slang-api/src/main/java/org/sonarsource/slang/api/HasKeyword.java new file mode 100644 index 00000000..a8185ad8 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/HasKeyword.java @@ -0,0 +1,26 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface HasKeyword { + + Token keyword(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/HasTextRange.java b/slang-api/src/main/java/org/sonarsource/slang/api/HasTextRange.java new file mode 100644 index 00000000..61e7f86b --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/HasTextRange.java @@ -0,0 +1,26 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface HasTextRange { + + TextRange textRange(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/IdentifierTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/IdentifierTree.java new file mode 100644 index 00000000..469f99e1 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/IdentifierTree.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface IdentifierTree extends Tree { + + String name(); + + // identifier is used for equivalence comparison + default String identifier() { + return name(); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/IfTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/IfTree.java new file mode 100644 index 00000000..a326ef7b --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/IfTree.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +/** + * The interface used to define conditional expressions. + * + * In order to be compatible with most of the languages, the 'IfTree' is not defined + * as a statement and should be considered as an expression. + * + * A 'IfTree' always has: + * - a keyword ('if', '?', 'unless', ...) + * - a condition + * - a 'then' branch + * + * Additionally, it's possible to also have: + * - an 'else' keyword + * - an 'else' branch (which does not necessarily requires a 'else' keyword) + * + * Known mapping from languages conditional expressions to IfTree: + * - Apex: if, ternary (a?b:c) + * - Ruby: if, ternary (a?b:c), unless (equivalent to 'if not') + * - Scala: if + */ +public interface IfTree extends Tree { + + Tree condition(); + + Tree thenBranch(); + + @CheckForNull + Tree elseBranch(); + + Token ifKeyword(); + + @CheckForNull + Token elseKeyword(); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ImportDeclarationTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ImportDeclarationTree.java new file mode 100644 index 00000000..9950f474 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ImportDeclarationTree.java @@ -0,0 +1,23 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface ImportDeclarationTree extends Tree { +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/IntegerLiteralTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/IntegerLiteralTree.java new file mode 100644 index 00000000..f0064015 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/IntegerLiteralTree.java @@ -0,0 +1,49 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.math.BigInteger; + +public interface IntegerLiteralTree extends LiteralTree { + + enum Base { + BINARY(2), + OCTAL(8), + DECIMAL(10), + HEXADECIMAL(16); + + private int radix; + + Base(int i) { + radix = i; + } + + public int getRadix() { + return radix; + } + } + + Base getBase(); + + BigInteger getIntegerValue(); + + String getNumericPart(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/JumpTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/JumpTree.java new file mode 100644 index 00000000..4b779f8f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/JumpTree.java @@ -0,0 +1,35 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface JumpTree extends Tree, HasKeyword { + enum JumpKind { + BREAK, + CONTINUE + } + @CheckForNull + IdentifierTree label(); + + Token keyword(); + + JumpKind kind(); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/LiteralTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/LiteralTree.java new file mode 100644 index 00000000..68709fa5 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/LiteralTree.java @@ -0,0 +1,26 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface LiteralTree extends Tree { + + String value(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/LoopTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/LoopTree.java new file mode 100644 index 00000000..28ec84b0 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/LoopTree.java @@ -0,0 +1,40 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface LoopTree extends Tree { + + public enum LoopKind { + FOR, + WHILE, + DOWHILE + } + + @CheckForNull + Tree condition(); + + Tree body(); + + LoopKind kind(); + + Token keyword(); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/MatchCaseTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/MatchCaseTree.java new file mode 100644 index 00000000..ce813064 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/MatchCaseTree.java @@ -0,0 +1,35 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface MatchCaseTree extends Tree { + + // expression is null in case of default clause + @CheckForNull + Tree expression(); + + @CheckForNull + Tree body(); + + TextRange rangeToHighlight(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/MatchTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/MatchTree.java new file mode 100644 index 00000000..be8354fb --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/MatchTree.java @@ -0,0 +1,34 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; +import javax.annotation.CheckForNull; + +public interface MatchTree extends Tree { + + @CheckForNull + Tree expression(); + + List cases(); + + Token keyword(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/MemberSelectTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/MemberSelectTree.java new file mode 100644 index 00000000..2f6979b8 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/MemberSelectTree.java @@ -0,0 +1,27 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface MemberSelectTree extends Tree { + + Tree expression(); + + IdentifierTree identifier(); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ModifierTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ModifierTree.java new file mode 100644 index 00000000..193b178b --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ModifierTree.java @@ -0,0 +1,33 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface ModifierTree extends Tree { + + enum Kind { + PUBLIC, + PRIVATE, + OVERRIDE, + PROTECTED + } + + Kind kind(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/NativeKind.java b/slang-api/src/main/java/org/sonarsource/slang/api/NativeKind.java new file mode 100644 index 00000000..80e31dce --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/NativeKind.java @@ -0,0 +1,23 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface NativeKind { +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/NativeTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/NativeTree.java new file mode 100644 index 00000000..b29bba81 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/NativeTree.java @@ -0,0 +1,26 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface NativeTree extends Tree { + + NativeKind nativeKind(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/PackageDeclarationTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/PackageDeclarationTree.java new file mode 100644 index 00000000..335c7864 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/PackageDeclarationTree.java @@ -0,0 +1,23 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface PackageDeclarationTree extends Tree { +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ParameterTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ParameterTree.java new file mode 100644 index 00000000..bc8f3efc --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ParameterTree.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; +import javax.annotation.CheckForNull; + +public interface ParameterTree extends Tree { + + @CheckForNull + IdentifierTree identifier(); + + @CheckForNull + Tree type(); + + @CheckForNull + Tree defaultValue(); + + List modifiers(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ParenthesizedExpressionTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ParenthesizedExpressionTree.java new file mode 100644 index 00000000..6d32d032 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ParenthesizedExpressionTree.java @@ -0,0 +1,30 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface ParenthesizedExpressionTree extends Tree { + + Tree expression(); + + Token leftParenthesis(); + + Token rightParenthesis(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ParseException.java b/slang-api/src/main/java/org/sonarsource/slang/api/ParseException.java new file mode 100644 index 00000000..dff64688 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ParseException.java @@ -0,0 +1,47 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class ParseException extends RuntimeException { + + private final transient TextPointer position; + + public ParseException(String message) { + this(message, null); + } + + public ParseException(String message, @Nullable TextPointer position) { + this(message, position, null); + } + + public ParseException(String message, @Nullable TextPointer position, @Nullable Throwable cause) { + super(message, cause); + this.position = position; + } + + @CheckForNull + public TextPointer getPosition() { + return position; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/PlaceHolderTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/PlaceHolderTree.java new file mode 100644 index 00000000..a269d159 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/PlaceHolderTree.java @@ -0,0 +1,26 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface PlaceHolderTree extends IdentifierTree { + + Token placeHolderToken(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ReturnTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ReturnTree.java new file mode 100644 index 00000000..9805e7a1 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ReturnTree.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface ReturnTree extends Tree, HasKeyword { + @CheckForNull + Tree body(); + + Token keyword(); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/StringLiteralTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/StringLiteralTree.java new file mode 100644 index 00000000..b3ab25f1 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/StringLiteralTree.java @@ -0,0 +1,26 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface StringLiteralTree extends LiteralTree { + + String content(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/TextPointer.java b/slang-api/src/main/java/org/sonarsource/slang/api/TextPointer.java new file mode 100644 index 00000000..3b8d136a --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/TextPointer.java @@ -0,0 +1,34 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface TextPointer extends Comparable { + + /** + * Starts at 1 + */ + int line(); + + /** + * Starts at 0 + */ + int lineOffset(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/TextRange.java b/slang-api/src/main/java/org/sonarsource/slang/api/TextRange.java new file mode 100644 index 00000000..0e44f0be --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/TextRange.java @@ -0,0 +1,32 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface TextRange { + + TextPointer start(); + + TextPointer end(); + + default boolean isInside(TextRange other) { + return this.start().compareTo(other.start()) >= 0 && this.end().compareTo(other.end()) <= 0; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/ThrowTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/ThrowTree.java new file mode 100644 index 00000000..65890ad6 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/ThrowTree.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface ThrowTree extends Tree, HasKeyword { + @CheckForNull + Tree body(); + + Token keyword(); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/Token.java b/slang-api/src/main/java/org/sonarsource/slang/api/Token.java new file mode 100644 index 00000000..6330d59c --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/Token.java @@ -0,0 +1,34 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface Token extends HasTextRange { + + public enum Type { + KEYWORD, + STRING_LITERAL, + OTHER + } + + String text(); + + Type type(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/TopLevelTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/TopLevelTree.java new file mode 100644 index 00000000..8f375ba9 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/TopLevelTree.java @@ -0,0 +1,34 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; +import javax.annotation.CheckForNull; + +public interface TopLevelTree extends Tree { + + List declarations(); + + List allComments(); + + @CheckForNull + Token firstCpdToken(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/Tree.java b/slang-api/src/main/java/org/sonarsource/slang/api/Tree.java new file mode 100644 index 00000000..4b36db01 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/Tree.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; +import java.util.stream.Stream; + +public interface Tree extends HasTextRange { + + List children(); + + TreeMetaData metaData(); + + @Override + default TextRange textRange() { + return metaData().textRange(); + } + + default Stream descendants() { + return children().stream() + .flatMap(tree -> Stream.concat(Stream.of(tree), tree.descendants())); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/TreeMetaData.java b/slang-api/src/main/java/org/sonarsource/slang/api/TreeMetaData.java new file mode 100644 index 00000000..3e32326f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/TreeMetaData.java @@ -0,0 +1,37 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.List; +import java.util.Set; + +public interface TreeMetaData { + + TextRange textRange(); + + List commentsInside(); + + List annotations(); + + List tokens(); + + Set linesOfCode(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/UnaryExpressionTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/UnaryExpressionTree.java new file mode 100644 index 00000000..3244eb59 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/UnaryExpressionTree.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +public interface UnaryExpressionTree extends Tree { + + public enum Operator { + NEGATE, + PLUS, + MINUS, + INCREMENT, + DECREMENT + } + + Operator operator(); + + Tree operand(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/VariableDeclarationTree.java b/slang-api/src/main/java/org/sonarsource/slang/api/VariableDeclarationTree.java new file mode 100644 index 00000000..a804ac10 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/VariableDeclarationTree.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import javax.annotation.CheckForNull; + +public interface VariableDeclarationTree extends Tree { + + IdentifierTree identifier(); + + @CheckForNull + Tree type(); + + @CheckForNull + Tree initializer(); + + boolean isVal(); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/api/package-info.java b/slang-api/src/main/java/org/sonarsource/slang/api/package-info.java new file mode 100644 index 00000000..770f8063 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/api/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.api; diff --git a/slang-api/src/main/java/org/sonarsource/slang/checks/api/CheckContext.java b/slang-api/src/main/java/org/sonarsource/slang/checks/api/CheckContext.java new file mode 100644 index 00000000..53863727 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/checks/api/CheckContext.java @@ -0,0 +1,60 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.api; + +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import java.util.Deque; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public interface CheckContext { + + Deque ancestors(); + + @CheckForNull + default Tree parent() { + if (this.ancestors().isEmpty()) { + return null; + } else { + return this.ancestors().peek(); + } + } + + String filename(); + + String fileContent(); + + void reportIssue(TextRange textRange, String message); + + void reportIssue(HasTextRange toHighlight, String message); + + void reportIssue(HasTextRange toHighlight, String message, SecondaryLocation secondaryLocation); + + void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations); + + void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations, @Nullable Double gap); + + void reportFileIssue(String message); + + void reportFileIssue(String message, @Nullable Double gap); +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/checks/api/InitContext.java b/slang-api/src/main/java/org/sonarsource/slang/checks/api/InitContext.java new file mode 100644 index 00000000..20d5cb25 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/checks/api/InitContext.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.api; + +import org.sonarsource.slang.api.Tree; +import java.util.function.BiConsumer; + +public interface InitContext { + + void register(Class cls, BiConsumer visitor); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/checks/api/SecondaryLocation.java b/slang-api/src/main/java/org/sonarsource/slang/checks/api/SecondaryLocation.java new file mode 100644 index 00000000..a49b66c4 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/checks/api/SecondaryLocation.java @@ -0,0 +1,46 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.api; + +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import javax.annotation.Nullable; + +public class SecondaryLocation { + + public final TextRange textRange; + + @Nullable + public final String message; + + public SecondaryLocation(Tree tree) { + this(tree, null); + } + + public SecondaryLocation(Tree tree, @Nullable String message) { + this(tree.metaData().textRange(), message); + } + + public SecondaryLocation(TextRange textRange, @Nullable String message) { + this.textRange = textRange; + this.message = message; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/checks/api/SlangCheck.java b/slang-api/src/main/java/org/sonarsource/slang/checks/api/SlangCheck.java new file mode 100644 index 00000000..111f3a6f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/checks/api/SlangCheck.java @@ -0,0 +1,26 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.api; + +public interface SlangCheck { + + void initialize(InitContext init); + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/checks/api/package-info.java b/slang-api/src/main/java/org/sonarsource/slang/checks/api/package-info.java new file mode 100644 index 00000000..f19a939d --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/checks/api/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.checks.api; diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/AnnotationImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/AnnotationImpl.java new file mode 100644 index 00000000..8b059500 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/AnnotationImpl.java @@ -0,0 +1,52 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.List; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.TextRange; + +public class AnnotationImpl implements Annotation { + + private final String shortName; + private final List argumentsText; + private final TextRange range; + + public AnnotationImpl(String shortName, List argumentsText, TextRange range) { + this.shortName = shortName; + this.argumentsText = argumentsText; + this.range = range; + } + + @Override + public String shortName() { + return shortName; + } + + @Override + public List argumentsText() { + return argumentsText; + } + + @Override + public TextRange textRange() { + return range; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/AssignmentExpressionTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/AssignmentExpressionTreeImpl.java new file mode 100644 index 00000000..00464e7e --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/AssignmentExpressionTreeImpl.java @@ -0,0 +1,60 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Arrays; +import java.util.List; + +public class AssignmentExpressionTreeImpl extends BaseTreeImpl implements AssignmentExpressionTree { + + private final Operator operator; + private final Tree leftHandSide; + private final Tree statementOrExpression; + + public AssignmentExpressionTreeImpl(TreeMetaData metaData, Operator operator, Tree leftHandSide, Tree statementOrExpression) { + super(metaData); + this.operator = operator; + this.leftHandSide = leftHandSide; + this.statementOrExpression = statementOrExpression; + } + + @Override + public Operator operator() { + return operator; + } + + @Override + public Tree leftHandSide() { + return leftHandSide; + } + + @Override + public Tree statementOrExpression() { + return statementOrExpression; + } + + @Override + public List children() { + return Arrays.asList(leftHandSide, statementOrExpression); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/BaseTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/BaseTreeImpl.java new file mode 100644 index 00000000..35eb8197 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/BaseTreeImpl.java @@ -0,0 +1,37 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public abstract class BaseTreeImpl implements Tree { + + private final TreeMetaData metaData; + + protected BaseTreeImpl(TreeMetaData metaData) { + this.metaData = metaData; + } + + @Override + public TreeMetaData metaData() { + return metaData; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/BinaryExpressionTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/BinaryExpressionTreeImpl.java new file mode 100644 index 00000000..7c8abb35 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/BinaryExpressionTreeImpl.java @@ -0,0 +1,69 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Arrays; +import java.util.List; + +public class BinaryExpressionTreeImpl extends BaseTreeImpl implements BinaryExpressionTree { + + private final Operator operator; + private final Token operatorToken; + private final Tree leftOperand; + private final Tree rightOperand; + + public BinaryExpressionTreeImpl(TreeMetaData metaData, Operator operator, Token operatorToken, Tree leftOperand, Tree rightOperand) { + super(metaData); + this.operator = operator; + this.operatorToken = operatorToken; + + this.leftOperand = leftOperand; + this.rightOperand = rightOperand; + } + + @Override + public Operator operator() { + return operator; + } + + @Override + public Token operatorToken() { + return operatorToken; + } + + @Override + public Tree leftOperand() { + return leftOperand; + } + + @Override + public Tree rightOperand() { + return rightOperand; + } + + @Override + public List children() { + return Arrays.asList(leftOperand, rightOperand); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/BlockTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/BlockTreeImpl.java new file mode 100644 index 00000000..c2a58c9d --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/BlockTreeImpl.java @@ -0,0 +1,45 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.List; + +public class BlockTreeImpl extends BaseTreeImpl implements BlockTree { + + private final List statementOrExpressions; + + public BlockTreeImpl(TreeMetaData metaData, List statementOrExpressions) { + super(metaData); + this.statementOrExpressions = statementOrExpressions; + } + + @Override + public List statementOrExpressions() { + return statementOrExpressions; + } + + @Override + public List children() { + return statementOrExpressions(); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/CatchTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/CatchTreeImpl.java new file mode 100644 index 00000000..6bbec5b7 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/CatchTreeImpl.java @@ -0,0 +1,72 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.CatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class CatchTreeImpl extends BaseTreeImpl implements CatchTree { + + private final Tree catchParameter; + private final Tree catchBlock; + private final Token keyword; + + public CatchTreeImpl(TreeMetaData metaData, @Nullable Tree catchParameter, Tree catchBlock, Token keyword) { + super(metaData); + this.catchParameter = catchParameter; + this.catchBlock = catchBlock; + this.keyword = keyword; + } + + @CheckForNull + @Override + public Tree catchParameter() { + return catchParameter; + } + + @Override + public Tree catchBlock() { + return catchBlock; + } + + @Override + public Token keyword() { + return keyword; + } + + @Override + public List children() { + List children = new ArrayList<>(); + + if (catchParameter != null) { + children.add(catchParameter); + } + children.add(catchBlock); + + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ClassDeclarationTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ClassDeclarationTreeImpl.java new file mode 100644 index 00000000..bc84abae --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ClassDeclarationTreeImpl.java @@ -0,0 +1,58 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class ClassDeclarationTreeImpl extends BaseTreeImpl implements ClassDeclarationTree { + + private final IdentifierTree identifier; + private final Tree classTree; + + public ClassDeclarationTreeImpl(TreeMetaData metaData, @Nullable IdentifierTree identifier, Tree classTree) { + super(metaData); + this.identifier = identifier; + this.classTree = classTree; + } + + @CheckForNull + @Override + public IdentifierTree identifier() { + return identifier; + } + + @Override + public Tree classTree() { + return classTree; + } + + @Override + public List children() { + // identifier is not added to the children as it is already part of this classTree structure + return Collections.singletonList(classTree); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/CommentImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/CommentImpl.java new file mode 100644 index 00000000..2993229f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/CommentImpl.java @@ -0,0 +1,59 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.TextRange; + +public class CommentImpl implements Comment { + + private final String text; + private final String contentText; + private final TextRange range; + private final TextRange contentRange; + + public CommentImpl(String text, String contentText, TextRange range, TextRange contentRange) { + this.contentText = contentText; + this.text = text; + this.range = range; + this.contentRange = contentRange; + } + + @Override + public String contentText() { + return contentText; + } + + @Override + public String text() { + return text; + } + + @Override + public TextRange textRange() { + return range; + } + + @Override + public TextRange contentRange() { + return contentRange; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ExceptionHandlingTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ExceptionHandlingTreeImpl.java new file mode 100644 index 00000000..30a35e49 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ExceptionHandlingTreeImpl.java @@ -0,0 +1,82 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.CatchTree; +import org.sonarsource.slang.api.ExceptionHandlingTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class ExceptionHandlingTreeImpl extends BaseTreeImpl implements ExceptionHandlingTree { + + private final Tree tryBlock; + private final List catchBlocks; + private final Tree finallyBlock; + private final Token tryKeyword; + + public ExceptionHandlingTreeImpl(TreeMetaData metaData, Tree tryBlock, Token tryKeyword, List catchBlocks, @Nullable Tree finallyBlock) { + super(metaData); + this.tryBlock = tryBlock; + this.catchBlocks = catchBlocks; + this.finallyBlock = finallyBlock; + this.tryKeyword = tryKeyword; + } + + @Override + public Tree tryBlock() { + return tryBlock; + } + + @Override + public List catchBlocks() { + return catchBlocks; + } + + @CheckForNull + @Override + public Tree finallyBlock() { + return finallyBlock; + } + + @Override + public Token tryKeyword() { + return tryKeyword; + } + + @Override + public List children() { + List children = new ArrayList<>(); + + children.add(tryBlock); + children.addAll(catchBlocks); + + if (finallyBlock != null) { + children.add(finallyBlock); + } + + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/FunctionDeclarationTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/FunctionDeclarationTreeImpl.java new file mode 100644 index 00000000..b2d751b3 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/FunctionDeclarationTreeImpl.java @@ -0,0 +1,145 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Collections; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class FunctionDeclarationTreeImpl extends BaseTreeImpl implements FunctionDeclarationTree { + + private List modifiers; + private final boolean isConstructor; + private final Tree returnType; + private final IdentifierTree name; + private final List formalParameters; + private final BlockTree body; + private final List children = new ArrayList<>(); + private final List nativeChildren; + + public FunctionDeclarationTreeImpl( + TreeMetaData metaData, + List modifiers, + boolean isConstructor, + @Nullable Tree returnType, + @Nullable IdentifierTree name, + List formalParameters, + @Nullable BlockTree body, + List nativeChildren) { + super(metaData); + + this.modifiers = modifiers; + this.isConstructor = isConstructor; + this.returnType = returnType; + this.name = name; + this.formalParameters = formalParameters; + this.body = body; + this.nativeChildren = nativeChildren; + + this.children.addAll(modifiers); + if (returnType != null) { + this.children.add(returnType); + } + if (name != null) { + this.children.add(name); + } + this.children.addAll(formalParameters); + if (body != null) { + this.children.add(body); + } + this.children.addAll(nativeChildren); + } + + @Override + public List modifiers() { + return Collections.unmodifiableList(modifiers); + } + + public void setModifiers(List modifiers) { + this.modifiers = modifiers; + } + + @Override + public boolean isConstructor() { + return isConstructor; + } + + @CheckForNull + @Override + public Tree returnType() { + return returnType; + } + + @CheckForNull + @Override + public IdentifierTree name() { + return name; + } + + @Override + public List formalParameters() { + return formalParameters; + } + + @CheckForNull + @Override + public BlockTree body() { + return body; + } + + @Override + public List nativeChildren() { + return nativeChildren; + } + + @Override + public TextRange rangeToHighlight() { + if (name != null) { + return name.metaData().textRange(); + } + if (body == null) { + return metaData().textRange(); + } + TextRange bodyRange = body.metaData().textRange(); + List tokenRangesBeforeBody = metaData().tokens().stream() + .map(Token::textRange) + .filter(t -> t.start().compareTo(bodyRange.start()) < 0) + .collect(Collectors.toList()); + if (tokenRangesBeforeBody.isEmpty()) { + return bodyRange; + } + return TextRanges.merge(tokenRangesBeforeBody); + } + + @Override + public List children() { + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/FunctionInvocationTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/FunctionInvocationTreeImpl.java new file mode 100644 index 00000000..194adc02 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/FunctionInvocationTreeImpl.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.ArrayList; +import java.util.List; +import org.sonarsource.slang.api.FunctionInvocationTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class FunctionInvocationTreeImpl extends BaseTreeImpl implements FunctionInvocationTree { + + private final Tree memberSelect; + private final List arguments; + + public FunctionInvocationTreeImpl(TreeMetaData metaData, Tree memberSelect, List arguments) { + super(metaData); + this.memberSelect = memberSelect; + this.arguments = arguments; + } + + + @Override + public List arguments() { + return arguments; + } + + @Override + public Tree memberSelect() { + return memberSelect; + } + + @Override + public List children() { + List children = new ArrayList<>(); + children.add(memberSelect); + children.addAll(arguments); + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/IdentifierTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/IdentifierTreeImpl.java new file mode 100644 index 00000000..baa2c9e8 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/IdentifierTreeImpl.java @@ -0,0 +1,45 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import java.util.List; + +public class IdentifierTreeImpl extends BaseTreeImpl implements IdentifierTree { + + private final String name; + + public IdentifierTreeImpl(TreeMetaData metaData, String name) { + super(metaData); + this.name = name; + } + + public String name() { + return name; + } + + @Override + public List children() { + return Collections.emptyList(); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/IfTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/IfTreeImpl.java new file mode 100644 index 00000000..85813511 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/IfTreeImpl.java @@ -0,0 +1,91 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.ArrayList; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class IfTreeImpl extends BaseTreeImpl implements IfTree { + + private final Tree condition; + private final Tree thenBranch; + private final Tree elseBranch; + private final Token ifKeyword; + private final Token elseKeyword; + + public IfTreeImpl( + TreeMetaData metaData, + Tree condition, + Tree thenBranch, + @Nullable Tree elseBranch, + Token ifKeyword, + @Nullable Token elseKeyword) { + super(metaData); + this.condition = condition; + this.thenBranch = thenBranch; + this.elseBranch = elseBranch; + this.ifKeyword = ifKeyword; + this.elseKeyword = elseKeyword; + } + + @Override + public Tree condition() { + return condition; + } + + @Override + public Tree thenBranch() { + return thenBranch; + } + + @CheckForNull + @Override + public Tree elseBranch() { + return elseBranch; + } + + @Override + public Token ifKeyword() { + return ifKeyword; + } + + @CheckForNull + @Override + public Token elseKeyword() { + return elseKeyword; + } + + @Override + public List children() { + List children = new ArrayList<>(); + children.add(condition); + children.add(thenBranch); + if (elseBranch != null) { + children.add(elseBranch); + } + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ImportDeclarationTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ImportDeclarationTreeImpl.java new file mode 100644 index 00000000..59740d66 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ImportDeclarationTreeImpl.java @@ -0,0 +1,42 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.List; +import org.sonarsource.slang.api.ImportDeclarationTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class ImportDeclarationTreeImpl extends BaseTreeImpl implements ImportDeclarationTree { + + private final List children; + + public ImportDeclarationTreeImpl(TreeMetaData metaData, List children) { + super(metaData); + this.children = children; + } + + @Override + public List children() { + return children; + } + + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/IntegerLiteralTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/IntegerLiteralTreeImpl.java new file mode 100644 index 00000000..6b42b6e5 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/IntegerLiteralTreeImpl.java @@ -0,0 +1,90 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.math.BigInteger; +import org.sonarsource.slang.api.IntegerLiteralTree; +import org.sonarsource.slang.api.TreeMetaData; + +/** + * Languages that can have integer literal with other bases, or who use a different syntax for binary/octal/decimal/hexadecimal + * values, specific plugins should provide its own implementation of {@link org.sonarsource.slang.api.IntegerLiteralTree} + */ +public class IntegerLiteralTreeImpl extends LiteralTreeImpl implements IntegerLiteralTree { + + private final Base base; + private final String numericPart; + + public IntegerLiteralTreeImpl(TreeMetaData metaData, String stringValue) { + super(metaData, stringValue); + + if (hasExplicitHexadecimalPrefix(stringValue)) { + base = IntegerLiteralTree.Base.HEXADECIMAL; + numericPart = stringValue.substring(2); + } else if (hasExplicitBinaryPrefix(stringValue)) { + base = IntegerLiteralTree.Base.BINARY; + numericPart = stringValue.substring(2); + } else if (hasExplicitDecimalPrefix(stringValue)) { + base = IntegerLiteralTree.Base.DECIMAL; + numericPart = stringValue.substring(2); + } else if (hasExplicitOctalPrefix(stringValue)) { + base = IntegerLiteralTree.Base.OCTAL; + numericPart = stringValue.substring(2); + } else if (!stringValue.equals("0") && stringValue.startsWith("0")) { + base = IntegerLiteralTree.Base.OCTAL; + numericPart = stringValue.substring(1); + } else { + base = Base.DECIMAL; + numericPart = stringValue; + } + } + + @Override + public Base getBase() { + return base; + } + + @Override + public BigInteger getIntegerValue() { + return new BigInteger(numericPart, base.getRadix()); + } + + @Override + public String getNumericPart() { + return numericPart; + } + + private static boolean hasExplicitOctalPrefix(String value) { + return value.startsWith("0o") || value.startsWith("0O"); + } + + private static boolean hasExplicitHexadecimalPrefix(String value) { + return value.startsWith("0x") || value.startsWith("0X"); + } + + private static boolean hasExplicitBinaryPrefix(String value) { + return value.startsWith("0b") || value.startsWith("0B"); + } + + private static boolean hasExplicitDecimalPrefix(String value) { + return value.startsWith("0d") || value.startsWith("0D"); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/JumpTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/JumpTreeImpl.java new file mode 100644 index 00000000..3acb35ce --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/JumpTreeImpl.java @@ -0,0 +1,64 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class JumpTreeImpl extends BaseTreeImpl implements JumpTree { + private final IdentifierTree label; + private final Token keyword; + private final JumpKind kind; + + public JumpTreeImpl(TreeMetaData metaData, Token keyword, JumpKind kind, @Nullable IdentifierTree label) { + super(metaData); + this.label = label; + this.keyword = keyword; + this.kind = kind; + } + + @CheckForNull + @Override + public IdentifierTree label() { + return label; + } + + @Override + public Token keyword() { + return keyword; + } + + @Override + public JumpKind kind() { + return kind; + } + + @Override + public List children() { + return label == null ? Collections.emptyList() : Collections.singletonList(label); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/LiteralTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/LiteralTreeImpl.java new file mode 100644 index 00000000..7d91433a --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/LiteralTreeImpl.java @@ -0,0 +1,47 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import java.util.List; + +public class LiteralTreeImpl extends BaseTreeImpl implements LiteralTree { + + private final String value; + + public LiteralTreeImpl(TreeMetaData metaData, String value) { + super(metaData); + this.value = value; + } + + @Override + public String value() { + return value; + } + + @Override + public List children() { + return Collections.emptyList(); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/LoopTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/LoopTreeImpl.java new file mode 100644 index 00000000..485c5f54 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/LoopTreeImpl.java @@ -0,0 +1,78 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.ArrayList; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +import java.util.List; + +public class LoopTreeImpl extends BaseTreeImpl implements LoopTree { + + private final Tree condition; + private final Tree body; + private final LoopKind kind; + private final Token keyword; + + public LoopTreeImpl(TreeMetaData metaData, @Nullable Tree condition, Tree body, LoopKind kind, Token keyword) { + super(metaData); + this.condition = condition; + this.body = body; + this.kind = kind; + this.keyword = keyword; + + } + + @CheckForNull + @Override + public Tree condition() { + return condition; + } + + @Override + public Tree body() { + return body; + } + + @Override + public LoopKind kind() { + return kind; + } + + @Override + public Token keyword() { + return keyword; + } + + @Override + public List children() { + List children = new ArrayList<>(); + if (condition != null) { + children.add(condition); + } + children.add(body); + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/MatchCaseTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/MatchCaseTreeImpl.java new file mode 100644 index 00000000..9fa47978 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/MatchCaseTreeImpl.java @@ -0,0 +1,86 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class MatchCaseTreeImpl extends BaseTreeImpl implements MatchCaseTree { + + private final Tree expression; + private final Tree body; + + public MatchCaseTreeImpl(TreeMetaData metaData, @Nullable Tree expression, @Nullable Tree body) { + super(metaData); + this.expression = expression; + this.body = body; + } + + @CheckForNull + @Override + public Tree expression() { + return expression; + } + + @CheckForNull + @Override + public Tree body() { + return body; + } + + @Override + public TextRange rangeToHighlight() { + if (body == null) { + return textRange(); + } + + TextRange bodyRange = body.metaData().textRange(); + List tokenRangesBeforeBody = metaData().tokens().stream() + .map(Token::textRange) + .filter(t -> t.start().compareTo(bodyRange.start()) < 0) + .collect(Collectors.toList()); + + // for ruby when body is empty, "when expr" is body meta, so there is nothing before + if (tokenRangesBeforeBody.isEmpty()) { + return bodyRange; + } + return TextRanges.merge(tokenRangesBeforeBody); + } + + @Override + public List children() { + List children = new ArrayList<>(); + if (expression != null) { + children.add(expression); + } + if (body != null) { + children.add(body); + } + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/MatchTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/MatchTreeImpl.java new file mode 100644 index 00000000..026f33b5 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/MatchTreeImpl.java @@ -0,0 +1,70 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.ArrayList; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class MatchTreeImpl extends BaseTreeImpl implements MatchTree { + + private final Tree expression; + private final List cases; + private final Token keyword; + + public MatchTreeImpl(TreeMetaData metaData, @Nullable Tree expression, List cases, Token keyword) { + super(metaData); + this.expression = expression; + this.cases = cases; + this.keyword = keyword; + } + + @CheckForNull + @Override + public Tree expression() { + return expression; + } + + @Override + public List cases() { + return cases; + } + + @Override + public Token keyword() { + return keyword; + } + + @Override + public List children() { + List children = new ArrayList<>(); + if (expression != null) { + children.add(expression); + } + children.addAll(cases); + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/MemberSelectTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/MemberSelectTreeImpl.java new file mode 100644 index 00000000..d539e2a8 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/MemberSelectTreeImpl.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.ArrayList; +import java.util.List; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.MemberSelectTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class MemberSelectTreeImpl extends BaseTreeImpl implements MemberSelectTree { + + private final Tree expression; + private final IdentifierTree identifier; + + public MemberSelectTreeImpl(TreeMetaData metaData, Tree expression, IdentifierTree identifier) { + super(metaData); + this.expression = expression; + this.identifier = identifier; + } + + @Override + public Tree expression() { + return expression; + } + + @Override + public IdentifierTree identifier() { + return identifier; + } + + @Override + public List children() { + List children = new ArrayList<>(); + children.add(expression); + children.add(identifier); + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ModifierTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ModifierTreeImpl.java new file mode 100644 index 00000000..5699f5f2 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ModifierTreeImpl.java @@ -0,0 +1,47 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import java.util.List; + +public class ModifierTreeImpl extends BaseTreeImpl implements ModifierTree { + + private final Kind kind; + + public ModifierTreeImpl(TreeMetaData metaData, Kind kind) { + super(metaData); + this.kind = kind; + } + + @Override + public Kind kind() { + return kind; + } + + @Override + public List children() { + return Collections.emptyList(); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/NativeTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/NativeTreeImpl.java new file mode 100644 index 00000000..b306abe7 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/NativeTreeImpl.java @@ -0,0 +1,48 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.List; + +public class NativeTreeImpl extends BaseTreeImpl implements NativeTree { + + private final NativeKind nativeKind; + private final List children; + + public NativeTreeImpl(TreeMetaData metaData, NativeKind nativeKind, List children) { + super(metaData); + this.nativeKind = nativeKind; + this.children = children; + } + + @Override + public NativeKind nativeKind() { + return nativeKind; + } + + @Override + public List children() { + return children; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/PackageDeclarationTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/PackageDeclarationTreeImpl.java new file mode 100644 index 00000000..e72ea104 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/PackageDeclarationTreeImpl.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.List; +import org.sonarsource.slang.api.PackageDeclarationTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class PackageDeclarationTreeImpl extends BaseTreeImpl implements PackageDeclarationTree { + + private final List children; + + public PackageDeclarationTreeImpl(TreeMetaData metaData, List children) { + super(metaData); + this.children = children; + } + + @Override + public List children() { + return children; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ParameterTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ParameterTreeImpl.java new file mode 100644 index 00000000..bc463811 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ParameterTreeImpl.java @@ -0,0 +1,93 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.ArrayList; +import java.util.Collections; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class ParameterTreeImpl extends BaseTreeImpl implements ParameterTree { + + private final IdentifierTree identifier; + private final Tree type; + private final Tree defaultValue; + private final List modifiers; + + public ParameterTreeImpl(TreeMetaData metaData, @Nullable IdentifierTree identifier, @Nullable Tree type, @Nullable Tree defaultValue, List modifiers) { + super(metaData); + this.identifier = identifier; + this.type = type; + this.defaultValue = defaultValue; + this.modifiers = modifiers; + } + + public ParameterTreeImpl(TreeMetaData metaData, @Nullable IdentifierTree identifier, @Nullable Tree type, @Nullable Tree defaultValue) { + this(metaData, identifier, type, defaultValue, Collections.emptyList()); + } + + public ParameterTreeImpl(TreeMetaData metaData, @Nullable IdentifierTree identifier, @Nullable Tree type) { + this(metaData, identifier, type, null); + } + + @Override + public IdentifierTree identifier() { + return identifier; + } + + @CheckForNull + @Override + public Tree type() { + return type; + } + + @CheckForNull + @Override + public Tree defaultValue() { + return defaultValue; + } + + @Override + public List modifiers() { + return modifiers; + } + + @Override + public List children() { + List children = new ArrayList<>(); + children.addAll(modifiers); + if (identifier != null) { + children.add(identifier); + } + if (type != null) { + children.add(type); + } + if (defaultValue != null) { + children.add(defaultValue); + } + return children; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ParenthesizedExpressionTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ParenthesizedExpressionTreeImpl.java new file mode 100644 index 00000000..440b22fd --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ParenthesizedExpressionTreeImpl.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.ParenthesizedExpressionTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import java.util.List; + +public class ParenthesizedExpressionTreeImpl extends BaseTreeImpl implements ParenthesizedExpressionTree { + + private final Tree expression; + private final Token leftParenthesis; + private final Token rightParenthesis; + + public ParenthesizedExpressionTreeImpl(TreeMetaData metaData, Tree expression, Token leftParenthesis, Token rightParenthesis) { + super(metaData); + this.expression = expression; + this.leftParenthesis = leftParenthesis; + this.rightParenthesis = rightParenthesis; + } + + @Override + public Tree expression() { + return expression; + } + + @Override + public Token leftParenthesis() { + return leftParenthesis; + } + + @Override + public Token rightParenthesis() { + return rightParenthesis; + } + + @Override + public List children() { + return Collections.singletonList(expression); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/PlaceHolderTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/PlaceHolderTreeImpl.java new file mode 100644 index 00000000..adb7426c --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/PlaceHolderTreeImpl.java @@ -0,0 +1,46 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Collections; +import java.util.List; +import org.sonarsource.slang.api.PlaceHolderTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class PlaceHolderTreeImpl extends IdentifierTreeImpl implements PlaceHolderTree { + private final Token placeHolderToken; + + public PlaceHolderTreeImpl(TreeMetaData metaData, Token placeHolderToken) { + super(metaData, "_"); + this.placeHolderToken = placeHolderToken; + } + + @Override + public Token placeHolderToken() { + return placeHolderToken; + } + + @Override + public List children() { + return Collections.emptyList(); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ReturnTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ReturnTreeImpl.java new file mode 100644 index 00000000..22f107fa --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ReturnTreeImpl.java @@ -0,0 +1,56 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.ReturnTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class ReturnTreeImpl extends BaseTreeImpl implements ReturnTree { + private final Tree body; + private final Token keyword; + + public ReturnTreeImpl(TreeMetaData metaData, Token keyword, @Nullable Tree body) { + super(metaData); + this.body = body; + this.keyword = keyword; + } + + @CheckForNull + @Override + public Tree body() { + return body; + } + + @Override + public Token keyword() { + return keyword; + } + + @Override + public List children() { + return body == null ? Collections.emptyList() : Collections.singletonList(body); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/StringLiteralTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/StringLiteralTreeImpl.java new file mode 100644 index 00000000..bc3869e4 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/StringLiteralTreeImpl.java @@ -0,0 +1,47 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.api.TreeMetaData; + +public class StringLiteralTreeImpl extends LiteralTreeImpl implements StringLiteralTree { + + private final String content; + + public StringLiteralTreeImpl(TreeMetaData metaData, String value) { + super(metaData, value); + if (value.length() < 2 || value.charAt(0) != '"' || value.charAt(value.length() - 1) != '"') { + throw new IllegalArgumentException("Invalid string format: expected \"XXX\""); + } + content = value().substring(1, value().length() - 1); + } + + public StringLiteralTreeImpl(TreeMetaData metaData, String value, String content) { + super(metaData, value); + this.content = content; + } + + @Override + public String content() { + return content; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/TextPointerImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/TextPointerImpl.java new file mode 100644 index 00000000..45afd1df --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/TextPointerImpl.java @@ -0,0 +1,71 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.TextPointer; +import java.util.Objects; + +public class TextPointerImpl implements TextPointer { + + private final int line; + private final int lineOffset; + + public TextPointerImpl(int line, int lineOffset) { + this.line = line; + this.lineOffset = lineOffset; + } + + @Override + public int line() { + return line; + } + + @Override + public int lineOffset() { + return lineOffset; + } + + @Override + public int compareTo(TextPointer other) { + int lineCompare = Integer.compare(this.line(), other.line()); + if (lineCompare != 0) { + return lineCompare; + } + return Integer.compare(this.lineOffset(), other.lineOffset()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TextPointerImpl that = (TextPointerImpl) o; + return line == that.line && lineOffset == that.lineOffset; + } + + @Override + public int hashCode() { + return Objects.hash(line, lineOffset); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/TextRangeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/TextRangeImpl.java new file mode 100644 index 00000000..992a7af8 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/TextRangeImpl.java @@ -0,0 +1,71 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import java.util.Objects; + +public class TextRangeImpl implements TextRange { + + private final TextPointer start; + private final TextPointer end; + + public TextRangeImpl (int startLine, int startLineOffset, int endLine, int endLineOffset) { + this(new TextPointerImpl(startLine, startLineOffset), new TextPointerImpl(endLine, endLineOffset)); + } + + public TextRangeImpl(TextPointer start, TextPointer end) { + this.start = start; + this.end = end; + } + + @Override + public TextPointer start() { + return start; + } + + @Override + public TextPointer end() { + return end; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TextRangeImpl textRange = (TextRangeImpl) o; + return Objects.equals(start, textRange.start) && Objects.equals(end, textRange.end); + } + + @Override + public int hashCode() { + return Objects.hash(start, end); + } + + @Override + public String toString() { + return "TextRange[" + start.line() + ", " + start.lineOffset() + ", " + end.line() + ", " + end.lineOffset() + ']'; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/TextRanges.java b/slang-api/src/main/java/org/sonarsource/slang/impl/TextRanges.java new file mode 100644 index 00000000..c42a3fc4 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/TextRanges.java @@ -0,0 +1,47 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.TextRange; +import java.util.List; +import java.util.function.Supplier; + +import static java.util.Comparator.naturalOrder; + +public class TextRanges { + + private static final Supplier MERGE_EXCEPTION_SUPPLIER = + () -> new IllegalArgumentException("Can't merge 0 ranges"); + + private TextRanges() { + } + + public static TextRange range(int startLine, int startLineOffset, int endLine, int endLineOffset) { + return new TextRangeImpl(startLine, startLineOffset, endLine, endLineOffset); + } + + public static TextRange merge(List ranges) { + return new TextRangeImpl( + ranges.stream().map(TextRange::start).min(naturalOrder()).orElseThrow(MERGE_EXCEPTION_SUPPLIER), + ranges.stream().map(TextRange::end).max(naturalOrder()).orElseThrow(MERGE_EXCEPTION_SUPPLIER) + ); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/ThrowTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/ThrowTreeImpl.java new file mode 100644 index 00000000..b6141e99 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/ThrowTreeImpl.java @@ -0,0 +1,56 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Collections; +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.ThrowTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class ThrowTreeImpl extends BaseTreeImpl implements ThrowTree { + private final Tree body; + private final Token keyword; + + public ThrowTreeImpl(TreeMetaData metaData, Token keyword, @Nullable Tree body) { + super(metaData); + this.body = body; + this.keyword = keyword; + } + + @CheckForNull + @Override + public Tree body() { + return body; + } + + @Override + public Token keyword() { + return keyword; + } + + @Override + public List children() { + return body == null ? Collections.emptyList() : Collections.singletonList(body); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/TokenImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/TokenImpl.java new file mode 100644 index 00000000..a1636bcf --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/TokenImpl.java @@ -0,0 +1,51 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; + +public class TokenImpl implements Token { + + private final TextRange textRange; + private final String text; + private final Type type; + + public TokenImpl(TextRange textRange, String text, Type type) { + this.textRange = textRange; + this.text = text; + this.type = type; + } + + @Override + public TextRange textRange() { + return textRange; + } + + @Override + public String text() { + return text; + } + + @Override + public Type type() { + return type; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/TopLevelTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/TopLevelTreeImpl.java new file mode 100644 index 00000000..0133f37f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/TopLevelTreeImpl.java @@ -0,0 +1,68 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +public class TopLevelTreeImpl extends BaseTreeImpl implements TopLevelTree { + + private final List declarations; + private final List allComments; + private final Token firstCpdToken; + + public TopLevelTreeImpl(TreeMetaData metaData, List declarations, List allComments) { + this(metaData, declarations, allComments, null); + } + + public TopLevelTreeImpl(TreeMetaData metaData, List declarations, List allComments, @Nullable Token firstCpdToken) { + super(metaData); + this.declarations = declarations; + this.allComments = allComments; + this.firstCpdToken = firstCpdToken; + } + + @Override + public List declarations() { + return declarations; + } + + @Override + public List allComments() { + return allComments; + } + + @CheckForNull + @Override + public Token firstCpdToken() { + return firstCpdToken; + } + + @Override + public List children() { + return declarations(); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/TreeMetaDataProvider.java b/slang-api/src/main/java/org/sonarsource/slang/impl/TreeMetaDataProvider.java new file mode 100644 index 00000000..d7ce1c20 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/TreeMetaDataProvider.java @@ -0,0 +1,235 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TreeMetaData; + +public class TreeMetaDataProvider { + + public static final Comparator COMPARATOR = Comparator.comparing(e -> e.textRange().start()); + + private final List sortedComments; + private final List sortedAnnotations; + private final List sortedTokens; + + public TreeMetaDataProvider(List comments, List tokens) { + this(comments, tokens, Collections.emptyList()); + } + + public TreeMetaDataProvider(List comments, List tokens, List annotations) { + this.sortedComments = new ArrayList<>(comments); + this.sortedComments.sort(COMPARATOR); + this.sortedTokens = new ArrayList<>(tokens); + this.sortedTokens.sort(COMPARATOR); + this.sortedAnnotations = new ArrayList<>(annotations); + this.sortedAnnotations.sort(COMPARATOR); + } + + public List allComments() { + return sortedComments; + } + + public List allTokens() { + return sortedTokens; + } + + public int indexOfFirstToken(TextRange textRange) { + return indexOfFirstElement(sortedTokens, textRange); + } + + public Optional firstToken(TextRange textRange) { + int textRangeIndex = indexOfFirstElement(sortedTokens, textRange); + if (textRangeIndex == -1) { + return Optional.empty(); + } else { + return Optional.of(sortedTokens.get(textRangeIndex)); + } + } + + public Optional previousToken(TextRange textRange) { + int textRangeIndex = indexOfFirstElement(sortedTokens, textRange); + if (textRangeIndex <= 0) { + return Optional.empty(); + } else { + return Optional.of(sortedTokens.get(textRangeIndex - 1)); + } + } + + public Optional previousToken(TextRange textRange, String expectedTokenValue) { + return previousToken(textRange, token -> expectedTokenValue.equals(token.text())); + } + + public Optional previousToken(TextRange textRange, Predicate expectedConditionToMatch) { + return previousToken(textRange).filter(expectedConditionToMatch::test); + } + + public void updateTokenType(Token token, Token.Type newType) { + int tokenIndex = indexOfFirstToken(token.textRange()); + if (!isExistingToken(token, tokenIndex)) { + throw new IllegalArgumentException("token '" + token.text() + "' not found in metadata, " + token.textRange()); + } + this.sortedTokens.set(tokenIndex, new TokenImpl(token.textRange(), token.text(), newType)); + } + + private boolean isExistingToken(Token token, int tokenIndex) { + return tokenIndex != -1 && this.sortedTokens.get(tokenIndex) == token; + } + + public Token keyword(TextRange textRange) { + List keywordsInRange = getElementsInRange(sortedTokens, textRange).stream() + .filter(t -> t.type() == Token.Type.KEYWORD) + .collect(Collectors.toList()); + if (keywordsInRange.size() != 1) { + throw new IllegalArgumentException("Cannot find single keyword in " + textRange); + } + return keywordsInRange.get(0); + } + + private static int indexOfFirstElement(List sortedList, TextRange textRange) { + HasTextRange key = () -> textRange; + int index = Collections.binarySearch(sortedList, key, COMPARATOR); + if (index < 0) { + index = -index - 1; + } + if (index < sortedList.size() && sortedList.get(index).textRange().isInside(textRange)) { + return index; + } + return -1; + } + + private static List getElementsInRange(List sortedList, TextRange textRange) { + int first = indexOfFirstElement(sortedList, textRange); + if (first == -1) { + return Collections.emptyList(); + } + List elementsInsideRange = new ArrayList<>(); + for (int i = first; i < sortedList.size(); i++) { + T element = sortedList.get(i); + if (!element.textRange().isInside(textRange)) { + break; + } + elementsInsideRange.add(element); + } + return elementsInsideRange; + } + + private static List getAnnotationStartingAtRange(List sortedList, List sortedToken, TextRange textRange) { + int first = indexOfFirstElement(sortedList, textRange); + if (first == -1) { + return Collections.emptyList(); + } + + List elementsInsideRange = new ArrayList<>(); + TextPointer currentPointer = textRange.start(); + + for (int i = first; i < sortedList.size(); i++) { + Annotation currentAnnotation = sortedList.get(i); + if (!currentAnnotation.textRange().start().equals(currentPointer)) { + break; + } + // We found a first annotation starting at the beginning of the text range. + elementsInsideRange.add(currentAnnotation); + // In addition, we also want all annotations that are just after the current one. + // A potential candidate is one starting at the position of the token following the current annotation. + int nextAnnotation = indexOfFirstElement(sortedToken, new TextRangeImpl(currentAnnotation.textRange().end(), textRange.end())); + if (nextAnnotation < 0) { + break; + } else { + currentPointer = sortedToken.get(nextAnnotation).textRange().start(); + } + } + + return elementsInsideRange; + } + + public TreeMetaData metaData(TextRange textRange) { + return new TreeMetaDataImpl(textRange); + } + + private class TreeMetaDataImpl implements TreeMetaData { + + private final TextRange textRange; + private Set linesOfCode; + private List annotations; + + private TreeMetaDataImpl(TextRange textRange) { + this.textRange = textRange; + } + + @Override + public TextRange textRange() { + return textRange; + } + + @Override + public List commentsInside() { + return getElementsInRange(sortedComments, textRange); + } + + @Override + public List annotations() { + if (annotations == null) { + annotations = getAnnotationStartingAtRange(sortedAnnotations, sortedTokens, textRange); + } + return annotations; + } + + @Override + public List tokens() { + return getElementsInRange(sortedTokens, textRange); + } + + @Override + public Set linesOfCode() { + if (linesOfCode == null) { + linesOfCode = computeLinesOfCode(); + } + return linesOfCode; + } + + private Set computeLinesOfCode() { + Set loc = new HashSet<>(); + for (Token token : tokens()) { + TextRange range = token.textRange(); + for (int i = range.start().line(); i <= range.end().line(); i++) { + loc.add(i); + } + } + return loc; + } + + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/UnaryExpressionTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/UnaryExpressionTreeImpl.java new file mode 100644 index 00000000..e66a60e3 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/UnaryExpressionTreeImpl.java @@ -0,0 +1,54 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.api.UnaryExpressionTree; +import java.util.Collections; +import java.util.List; + +public class UnaryExpressionTreeImpl extends BaseTreeImpl implements UnaryExpressionTree { + + private final Operator operator; + private final Tree operand; + + public UnaryExpressionTreeImpl(TreeMetaData metaData, Operator operator, Tree operand) { + super(metaData); + this.operator = operator; + this.operand = operand; + } + + @Override + public Operator operator() { + return operator; + } + + @Override + public Tree operand() { + return operand; + } + + @Override + public List children() { + return Collections.singletonList(operand); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/VariableDeclarationTreeImpl.java b/slang-api/src/main/java/org/sonarsource/slang/impl/VariableDeclarationTreeImpl.java new file mode 100644 index 00000000..a053add0 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/VariableDeclarationTreeImpl.java @@ -0,0 +1,86 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.api.VariableDeclarationTree; + + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.List; + +public class VariableDeclarationTreeImpl extends BaseTreeImpl implements VariableDeclarationTree { + + private final IdentifierTree identifier; + private final Tree type; + private final Tree initializer; + private final boolean isVal; + + public VariableDeclarationTreeImpl(TreeMetaData metaData, IdentifierTree identifier, @Nullable Tree type, @Nullable Tree initializer, boolean isVal) { + super(metaData); + this.identifier = identifier; + this.type = type; + this.initializer = initializer; + this.isVal = isVal; + } + + @Override + public IdentifierTree identifier() { + return identifier; + } + + @CheckForNull + @Override + public Tree type() { + return type; + } + + @CheckForNull + @Override + public Tree initializer() { + return initializer; + } + + @Override + public boolean isVal() { + return isVal; + } + + @Override + public List children() { + List children = new ArrayList<>(); + children.add(identifier); + + if (type != null) { + children.add(type); + } + + if (initializer != null) { + children.add(initializer); + } + + return children; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/impl/package-info.java b/slang-api/src/main/java/org/sonarsource/slang/impl/package-info.java new file mode 100644 index 00000000..1ce7ae85 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/impl/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.impl; diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/JsonTree.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/JsonTree.java new file mode 100644 index 00000000..2f3a8988 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/JsonTree.java @@ -0,0 +1,55 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonObject; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.TreeMetaDataProvider; +import org.sonarsource.slang.persistence.conversion.DeserializationContext; +import org.sonarsource.slang.persistence.conversion.JsonTreeConverter; +import org.sonarsource.slang.persistence.conversion.SerializationContext; + +public final class JsonTree { + + private JsonTree() { + } + + public static String toJson(Tree tree) { + TreeMetaData metaData = tree.metaData(); + TreeMetaDataProvider provider = new TreeMetaDataProvider(metaData.commentsInside(), metaData.tokens()); + SerializationContext ctx = new SerializationContext(JsonTreeConverter.POLYMORPHIC_CONVERTER); + return Json.object() + .add("treeMetaData", JsonTreeConverter.TREE_METADATA_PROVIDER_TO_JSON.apply(ctx, provider)) + .add("tree", ctx.toJson(tree)) + .toString(); + } + + public static Tree fromJson(String json) { + JsonObject root = Json.parse(json).asObject(); + JsonObject treeMetaData = root.get("treeMetaData").asObject(); + DeserializationContext ctx = new DeserializationContext(JsonTreeConverter.POLYMORPHIC_CONVERTER); + TreeMetaDataProvider metaDataProvider = JsonTreeConverter.TREE_METADATA_PROVIDER_FROM_JSON.apply(ctx, treeMetaData); + ctx = ctx.withMetaDataProvider(metaDataProvider); + return ctx.fieldToNullableObject(root, "tree", Tree.class); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/DeserializationContext.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/DeserializationContext.java new file mode 100644 index 00000000..c43b89fe --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/DeserializationContext.java @@ -0,0 +1,184 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonValue; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.TreeMetaDataProvider; + +public class DeserializationContext { + + private static final int MAX_ILLEGAL_ELEMENT_TEXT_LENGTH = 80; + + private final PolymorphicConverter polymorphicConverter; + + private final Deque jsonPath = new LinkedList<>(); + + private TreeMetaDataProvider metaDataProvider = null; + + public DeserializationContext(PolymorphicConverter polymorphicConverter) { + this.polymorphicConverter = polymorphicConverter; + } + + public DeserializationContext withMetaDataProvider(TreeMetaDataProvider metaDataProvider) { + this.metaDataProvider = metaDataProvider; + return this; + } + + public void pushPath(String fieldName) { + jsonPath.addLast(fieldName); + } + + public void popPath() { + jsonPath.removeLast(); + } + + public String path() { + return String.join("/", jsonPath); + } + + public TreeMetaData metaData(JsonObject json) { + return RangeConverter.resolveMetaData(metaDataProvider, fieldToString(json, "metaData")); + } + + public RuntimeException newIllegalMemberException(String message, @Nullable Object illegalElement) { + String elementText = String.valueOf(illegalElement); + elementText = elementText.substring(0, Math.min(elementText.length(), MAX_ILLEGAL_ELEMENT_TEXT_LENGTH)); + return new IllegalStateException(message + " at '" + path() + "' member: " + elementText); + } + + @Nullable + public T fieldToNullableObject(JsonObject parent, String fieldName, Class expectedClass) { + JsonValue json = parent.get(fieldName); + if (json == null || Json.NULL.equals(json)) { + return null; + } + return object(json, fieldName, expectedClass); + } + + public T fieldToObject(JsonObject parent, String fieldName, Class expectedClass) { + JsonValue json = parent.get(fieldName); + if (json == null || Json.NULL.equals(json)) { + throw newIllegalMemberException("Unexpected null value for field '" + fieldName + "'", json); + } + return object(json, fieldName, expectedClass); + } + + public NativeKind fieldToNativeKind(JsonObject parent, String fieldName) { + return StringNativeKind.of(fieldToString(parent, fieldName, "")); + } + + public > T fieldToEnum(JsonObject parent, String fieldName, Class enumType) { + return Enum.valueOf(enumType, fieldToString(parent, fieldName)); + } + + public > T fieldToEnum(JsonObject parent, String fieldName, String defaultValue, Class enumType) { + return Enum.valueOf(enumType, fieldToString(parent, fieldName, defaultValue)); + } + + public List fieldToObjectList(JsonObject parent, String fieldName, Class expectedClass) { + return objectList(parent.get(fieldName), fieldName + "[]", expectedClass); + } + + public List objectList(@Nullable JsonValue value, String memberName, Class expectedClass) { + return objectList(value, jsonChild -> object(jsonChild, memberName, expectedClass)); + } + + public List objectList(@Nullable JsonValue value, BiFunction converter) { + return objectList(value, jsonChild -> converter.apply(this, jsonChild)); + } + + private List objectList(@Nullable JsonValue value, Function converter) { + if (value == null || value.isNull()) { + return Collections.emptyList(); + } + if (!value.isArray()) { + throw newIllegalMemberException("Expect Array instead of " + value.getClass().getSimpleName(), value); + } + List result = new ArrayList<>(); + for (JsonValue jsonValue : value.asArray()) { + result.add(converter.apply(jsonValue.asObject())); + } + return result; + } + + public String fieldToNullableString(JsonObject json, String fieldName) { + JsonValue value = json.get(fieldName); + if (value == null || Json.NULL.equals(value)) { + return null; + } + return fieldToString(json, fieldName); + } + + public String fieldToString(JsonObject json, String fieldName) { + JsonValue value = json.get(fieldName); + if (value == null || Json.NULL.equals(value)) { + throw newIllegalMemberException("Missing non-null value for field '" + fieldName + "'", json); + } + if (!value.isString()) { + throw newIllegalMemberException("Expect String instead of '" + value.getClass().getSimpleName() + + "' for field '" + fieldName + "'", json); + } + return value.asString(); + } + + public String fieldToString(JsonObject json, String fieldName, String defaultValue){ + return json.getString(fieldName, defaultValue); + } + + public TextRange fieldToRange(JsonObject json, String fieldName) { + return RangeConverter.parse(fieldToString(json, fieldName)); + } + + public Token fieldToToken(JsonObject json, String fieldName) { + return RangeConverter.resolveToken(metaDataProvider, fieldToString(json, fieldName)); + } + + @Nullable + public Token fieldToNullableToken(JsonObject json, String fieldName) { + return RangeConverter.resolveToken(metaDataProvider, fieldToNullableString(json, fieldName)); + } + + private T object(JsonValue json, String memberName, Class expectedClass) { + pushPath(memberName); + if (!json.isObject()) { + throw newIllegalMemberException("Unexpected value for Tree", json); + } + JsonObject jsonObject = json.asObject(); + String jsonType = fieldToString(jsonObject, SerializationContext.TYPE_ATTRIBUTE); + T object = polymorphicConverter.fromJson(this, jsonType, jsonObject, memberName, expectedClass); + popPath(); + return object; + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/JsonTreeConverter.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/JsonTreeConverter.java new file mode 100644 index 00000000..c3f504eb --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/JsonTreeConverter.java @@ -0,0 +1,530 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonObject; +import java.util.List; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree.Operator; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.CatchTree; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.impl.AssignmentExpressionTreeImpl; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.BlockTreeImpl; +import org.sonarsource.slang.impl.CatchTreeImpl; +import org.sonarsource.slang.impl.ClassDeclarationTreeImpl; +import org.sonarsource.slang.impl.CommentImpl; +import org.sonarsource.slang.impl.ExceptionHandlingTreeImpl; +import org.sonarsource.slang.impl.FunctionDeclarationTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.IfTreeImpl; +import org.sonarsource.slang.impl.ImportDeclarationTreeImpl; +import org.sonarsource.slang.impl.IntegerLiteralTreeImpl; +import org.sonarsource.slang.impl.JumpTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.LoopTreeImpl; +import org.sonarsource.slang.impl.MatchCaseTreeImpl; +import org.sonarsource.slang.impl.MatchTreeImpl; +import org.sonarsource.slang.impl.ModifierTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.PackageDeclarationTreeImpl; +import org.sonarsource.slang.impl.ParameterTreeImpl; +import org.sonarsource.slang.impl.ParenthesizedExpressionTreeImpl; +import org.sonarsource.slang.impl.PlaceHolderTreeImpl; +import org.sonarsource.slang.impl.ReturnTreeImpl; +import org.sonarsource.slang.impl.StringLiteralTreeImpl; +import org.sonarsource.slang.impl.ThrowTreeImpl; +import org.sonarsource.slang.impl.TokenImpl; +import org.sonarsource.slang.impl.TopLevelTreeImpl; +import org.sonarsource.slang.impl.TreeMetaDataProvider; +import org.sonarsource.slang.impl.UnaryExpressionTreeImpl; +import org.sonarsource.slang.impl.VariableDeclarationTreeImpl; +import org.sonarsource.slang.persistence.conversion.PolymorphicConverter.Deserialize; +import org.sonarsource.slang.persistence.conversion.PolymorphicConverter.Serialize; + +public final class JsonTreeConverter { + + public static final String BODY = "body"; + public static final String CASES = "cases"; + public static final String CATCH_BLOCK = "catchBlock"; + public static final String CATCH_BLOCKS = "catchBlocks"; + public static final String CATCH_PARAMETER = "catchParameter"; + public static final String CHILDREN = "children"; + public static final String CLASS_TREE = "classTree"; + public static final String COMMENTS = "comments"; + public static final String CONDITION = "condition"; + public static final String CONTENT = "content"; + public static final String CONTENT_RANGE = "contentRange"; + public static final String CONTENT_TEXT = "contentText"; + public static final String DECLARATIONS = "declarations"; + public static final String DEFAULT_VALUE = "defaultValue"; + public static final String ELSE_BRANCH = "elseBranch"; + public static final String ELSE_KEYWORD = "elseKeyword"; + public static final String EXPRESSION = "expression"; + public static final String FINALLY_BLOCK = "finallyBlock"; + public static final String FIRST_CPD_TOKEN = "firstCpdToken"; + public static final String FORMAL_PARAMETERS = "formalParameters"; + public static final String IDENTIFIER = "identifier"; + public static final String IF_KEYWORD = "ifKeyword"; + public static final String INITIALIZER = "initializer"; + public static final String IS_CONSTRUCTOR = "isConstructor"; + public static final String IS_VAL = "isVal"; + public static final String KEYWORD = "keyword"; + public static final String KIND = "kind"; + public static final String LABEL = "label"; + public static final String LEFT_HAND_SIDE = "leftHandSide"; + public static final String LEFT_OPERAND = "leftOperand"; + public static final String LEFT_PARENTHESIS = "leftParenthesis"; + public static final String MODIFIERS = "modifiers"; + public static final String NAME = "name"; + public static final String NATIVE_CHILDREN = "nativeChildren"; + public static final String NATIVE_KIND = "nativeKind"; + public static final String OPERAND = "operand"; + public static final String OPERATOR = "operator"; + public static final String OPERATOR_TOKEN = "operatorToken"; + public static final String PLACE_HOLDER_TOKEN = "placeHolderToken"; + public static final String RANGE = "range"; + public static final String RETURN_TYPE = "returnType"; + public static final String RIGHT_OPERAND = "rightOperand"; + public static final String RIGHT_PARENTHESIS = "rightParenthesis"; + public static final String STATEMENT_OR_EXPRESSION = "statementOrExpression"; + public static final String STATEMENT_OR_EXPRESSIONS = "statementOrExpressions"; + public static final String TEXT = "text"; + public static final String TEXT_RANGE = "textRange"; + public static final String THEN_BRANCH = "thenBranch"; + public static final String TOKENS = "tokens"; + public static final String TRY_BLOCK = "tryBlock"; + public static final String TRY_KEYWORD = "tryKeyword"; + public static final String TYPE = "type"; + public static final String VALUE = "value"; + public static final String OTHER = "OTHER"; + + public static final PolymorphicConverter POLYMORPHIC_CONVERTER = new PolymorphicConverter(); + + public static final Serialize TOKEN_TO_JSON = (ctx, token) -> { + JsonObject json = Json.object() + .add(TEXT_RANGE, ctx.toJson(token.textRange())) + .add(TEXT, token.text()); + + return token.type().name().equals(OTHER) + ? json + : json.add(TYPE, ctx.toJson(token.type())); + }; + + public static final Deserialize TOKEN_FROM_JSON = (ctx, json) -> new TokenImpl( + ctx.fieldToRange(json, TEXT_RANGE), + ctx.fieldToString(json, TEXT), + ctx.fieldToEnum(json, TYPE, OTHER, Token.Type.class)); + + public static final Serialize COMMENT_TO_JSON = (ctx, comment) -> Json.object() + .add(TEXT, comment.text()) + .add(CONTENT_TEXT, comment.contentText()) + .add(RANGE, ctx.toJson(comment.textRange())) + .add(CONTENT_RANGE, ctx.toJson(comment.contentRange())); + + public static final Deserialize COMMENT_FROM_JSON = (ctx, json) -> new CommentImpl( + ctx.fieldToString(json, TEXT), + ctx.fieldToString(json, CONTENT_TEXT), + ctx.fieldToRange(json, RANGE), + ctx.fieldToRange(json, CONTENT_RANGE)); + + public static final Serialize TREE_METADATA_PROVIDER_TO_JSON = (ctx, provider) -> Json.object() + .add(COMMENTS, ctx.toJsonArray(provider.allComments(), COMMENT_TO_JSON)) + .add(TOKENS, ctx.toJsonArray(provider.allTokens(), TOKEN_TO_JSON)); + + public static final Deserialize TREE_METADATA_PROVIDER_FROM_JSON = (ctx, json) -> new TreeMetaDataProvider( + ctx.objectList(json.get(COMMENTS), COMMENT_FROM_JSON), + ctx.objectList(json.get(TOKENS), TOKEN_FROM_JSON)); + + static { + + register(AssignmentExpressionTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(OPERATOR, ctx.toJson(tree.operator())) + .add(LEFT_HAND_SIDE, ctx.toJson(tree.leftHandSide())) + .add(STATEMENT_OR_EXPRESSION, ctx.toJson(tree.statementOrExpression())), + + (ctx, json) -> new AssignmentExpressionTreeImpl( + ctx.metaData(json), + ctx.fieldToEnum(json, OPERATOR, AssignmentExpressionTree.Operator.class), + ctx.fieldToObject(json, LEFT_HAND_SIDE, Tree.class), + ctx.fieldToObject(json, STATEMENT_OR_EXPRESSION, Tree.class))); + + register(BinaryExpressionTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(OPERATOR, ctx.toJson(tree.operator())) + .add(OPERATOR_TOKEN, ctx.toJson(tree.operatorToken())) + .add(LEFT_OPERAND, ctx.toJson(tree.leftOperand())) + .add(RIGHT_OPERAND, ctx.toJson(tree.rightOperand())), + + (ctx, json) -> new BinaryExpressionTreeImpl( + ctx.metaData(json), + ctx.fieldToEnum(json, OPERATOR, Operator.class), + ctx.fieldToToken(json, OPERATOR_TOKEN), + ctx.fieldToObject(json, LEFT_OPERAND, Tree.class), + ctx.fieldToObject(json, RIGHT_OPERAND, Tree.class))); + + register(BlockTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(STATEMENT_OR_EXPRESSIONS, ctx.toJsonArray(tree.children())), + + (ctx, json) -> new BlockTreeImpl( + ctx.metaData(json), + ctx.fieldToObjectList(json, STATEMENT_OR_EXPRESSIONS, Tree.class))); + + register(CatchTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(CATCH_PARAMETER, ctx.toJson(tree.catchParameter())) + .add(CATCH_BLOCK, ctx.toJson(tree.catchBlock())) + .add(KEYWORD, ctx.toJson(tree.keyword())), + + (ctx, json) -> new CatchTreeImpl( + ctx.metaData(json), + ctx.fieldToNullableObject(json, CATCH_PARAMETER, Tree.class), + ctx.fieldToObject(json, CATCH_BLOCK, Tree.class), + ctx.fieldToToken(json, KEYWORD))); + + register(ClassDeclarationTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(IDENTIFIER, RangeConverter.treeReference(tree.identifier())) + .add(CLASS_TREE, ctx.toJson(tree.classTree())), + + (ctx, json) -> { + Tree classTree = ctx.fieldToObject(json, CLASS_TREE, Tree.class); + String identifierReference = ctx.fieldToNullableString(json, IDENTIFIER); + IdentifierTree identifier = RangeConverter.resolveNullableTree(classTree, identifierReference, IdentifierTree.class); + return new ClassDeclarationTreeImpl( + ctx.metaData(json), + identifier, + classTree); + }); + + register(ExceptionHandlingTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(TRY_BLOCK, ctx.toJson(tree.tryBlock())) + .add(TRY_KEYWORD, ctx.toJson(tree.tryKeyword())) + .add(CATCH_BLOCKS, ctx.toJsonArray(tree.catchBlocks())) + .add(FINALLY_BLOCK, ctx.toJson(tree.finallyBlock())), + + (ctx, json) -> new ExceptionHandlingTreeImpl( + ctx.metaData(json), + ctx.fieldToObject(json, TRY_BLOCK, Tree.class), + ctx.fieldToToken(json, TRY_KEYWORD), + ctx.fieldToObjectList(json, CATCH_BLOCKS, CatchTree.class), + ctx.fieldToNullableObject(json, FINALLY_BLOCK, Tree.class))); + + register(FunctionDeclarationTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(MODIFIERS, ctx.toJsonArray(tree.modifiers())) + .add(IS_CONSTRUCTOR, tree.isConstructor()) + .add(RETURN_TYPE, ctx.toJson(tree.returnType())) + .add(NAME, ctx.toJson(tree.name())) + .add(FORMAL_PARAMETERS, ctx.toJsonArray(tree.formalParameters())) + .add(BODY, ctx.toJson(tree.body())) + .add(NATIVE_CHILDREN, ctx.toJsonArray(tree.nativeChildren())), + + (ctx, json) -> new FunctionDeclarationTreeImpl( + ctx.metaData(json), + ctx.fieldToObjectList(json, MODIFIERS, Tree.class), + json.getBoolean(IS_CONSTRUCTOR, false), + ctx.fieldToNullableObject(json, RETURN_TYPE, Tree.class), + ctx.fieldToNullableObject(json, NAME, IdentifierTree.class), + ctx.fieldToObjectList(json, FORMAL_PARAMETERS, Tree.class), + ctx.fieldToNullableObject(json, BODY, BlockTree.class), + ctx.fieldToObjectList(json, NATIVE_CHILDREN, Tree.class))); + + register(IdentifierTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(NAME, tree.name()), + + (ctx, json) -> new IdentifierTreeImpl( + ctx.metaData(json), + ctx.fieldToString(json, NAME))); + + register(IfTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(CONDITION, ctx.toJson(tree.condition())) + .add(THEN_BRANCH, ctx.toJson(tree.thenBranch())) + .add(ELSE_BRANCH, ctx.toJson(tree.elseBranch())) + .add(IF_KEYWORD, ctx.toJson(tree.ifKeyword())) + .add(ELSE_KEYWORD, ctx.toJson(tree.elseKeyword())), + + (ctx, json) -> new IfTreeImpl( + ctx.metaData(json), + ctx.fieldToObject(json, CONDITION, Tree.class), + ctx.fieldToObject(json, THEN_BRANCH, Tree.class), + ctx.fieldToNullableObject(json, ELSE_BRANCH, Tree.class), + ctx.fieldToToken(json, IF_KEYWORD), + ctx.fieldToNullableToken(json, ELSE_KEYWORD))); + + register(ImportDeclarationTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(CHILDREN, ctx.toJsonArray(tree.children())), + + (ctx, json) -> new ImportDeclarationTreeImpl( + ctx.metaData(json), + ctx.fieldToObjectList(json, CHILDREN, Tree.class))); + + register(IntegerLiteralTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(VALUE, tree.value()), + + (ctx, json) -> new IntegerLiteralTreeImpl( + ctx.metaData(json), + ctx.fieldToString(json, VALUE))); + + register(JumpTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(LABEL, ctx.toJson(tree.label())) + .add(KEYWORD, ctx.toJson(tree.keyword())) + .add(KIND, ctx.toJson(tree.kind())), + + (ctx, json) -> new JumpTreeImpl( + ctx.metaData(json), + ctx.fieldToToken(json, KEYWORD), + ctx.fieldToEnum(json, KIND, JumpTree.JumpKind.class), + ctx.fieldToNullableObject(json, LABEL, IdentifierTree.class))); + + register(LiteralTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(VALUE, tree.value()), + + (ctx, json) -> new LiteralTreeImpl( + ctx.metaData(json), + ctx.fieldToString(json, VALUE))); + + register(LoopTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(CONDITION, ctx.toJson(tree.condition())) + .add(BODY, ctx.toJson(tree.body())) + .add(KIND, ctx.toJson(tree.kind())) + .add(KEYWORD, ctx.toJson(tree.keyword())), + + (ctx, json) -> new LoopTreeImpl( + ctx.metaData(json), + ctx.fieldToNullableObject(json, CONDITION, Tree.class), + ctx.fieldToObject(json, BODY, Tree.class), + ctx.fieldToEnum(json, KIND, LoopTree.LoopKind.class), + ctx.fieldToToken(json, KEYWORD))); + + register(MatchTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(EXPRESSION, ctx.toJson(tree.expression())) + .add(CASES, ctx.toJsonArray(tree.cases())) + .add(KEYWORD, ctx.toJson(tree.keyword())), + + (ctx, json) -> new MatchTreeImpl( + ctx.metaData(json), + ctx.fieldToNullableObject(json, EXPRESSION, Tree.class), + ctx.fieldToObjectList(json, CASES, MatchCaseTree.class), + ctx.fieldToToken(json, KEYWORD))); + + register(MatchCaseTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(EXPRESSION, ctx.toJson(tree.expression())) + .add(BODY, ctx.toJson(tree.body())), + + (ctx, json) -> new MatchCaseTreeImpl( + ctx.metaData(json), + ctx.fieldToNullableObject(json, EXPRESSION, Tree.class), + ctx.fieldToNullableObject(json, BODY, Tree.class))); + + register(ModifierTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(KIND, ctx.toJson(tree.kind())), + + (ctx, json) -> new ModifierTreeImpl( + ctx.metaData(json), + ctx.fieldToEnum(json, KIND, ModifierTree.Kind.class))); + + register(NativeTreeImpl.class, + + (ctx, tree) -> { + JsonObject json = ctx.newTypedObject(tree); + + if (!tree.nativeKind().toString().isEmpty()){ + json.add(NATIVE_KIND, ctx.toJson(tree.nativeKind())); + } + + return json.add(CHILDREN, ctx.toJsonArray(tree.children())); + }, + + (ctx, json) -> new NativeTreeImpl( + ctx.metaData(json), + ctx.fieldToNativeKind(json, NATIVE_KIND), + ctx.fieldToObjectList(json, CHILDREN, Tree.class))); + + register(PackageDeclarationTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(CHILDREN, ctx.toJsonArray(tree.children())), + + (ctx, json) -> new PackageDeclarationTreeImpl( + ctx.metaData(json), + ctx.fieldToObjectList(json, CHILDREN, Tree.class))); + + register(ParameterTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(IDENTIFIER, ctx.toJson(tree.identifier())) + .add(TYPE, ctx.toJson(tree.type())) + .add(DEFAULT_VALUE, ctx.toJson(tree.defaultValue())) + .add(MODIFIERS, ctx.toJsonArray(tree.modifiers())), + + (ctx, json) -> new ParameterTreeImpl( + ctx.metaData(json), + ctx.fieldToObject(json, IDENTIFIER, IdentifierTree.class), + ctx.fieldToNullableObject(json, TYPE, Tree.class), + ctx.fieldToNullableObject(json, DEFAULT_VALUE, Tree.class), + ctx.fieldToObjectList(json, MODIFIERS, Tree.class))); + + register(ParenthesizedExpressionTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(EXPRESSION, ctx.toJson(tree.expression())) + .add(LEFT_PARENTHESIS, ctx.toJson(tree.leftParenthesis())) + .add(RIGHT_PARENTHESIS, ctx.toJson(tree.rightParenthesis())), + + (ctx, json) -> new ParenthesizedExpressionTreeImpl( + ctx.metaData(json), + ctx.fieldToObject(json, EXPRESSION, Tree.class), + ctx.fieldToToken(json, LEFT_PARENTHESIS), + ctx.fieldToToken(json, RIGHT_PARENTHESIS))); + + register(PlaceHolderTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(PLACE_HOLDER_TOKEN, ctx.toJson(tree.placeHolderToken())), + + (ctx, json) -> new PlaceHolderTreeImpl( + ctx.metaData(json), + ctx.fieldToToken(json, PLACE_HOLDER_TOKEN))); + + register(ReturnTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(BODY, ctx.toJson(tree.body())) + .add(KEYWORD, ctx.toJson(tree.keyword())), + + (ctx, json) -> new ReturnTreeImpl( + ctx.metaData(json), + ctx.fieldToToken(json, KEYWORD), + ctx.fieldToNullableObject(json, BODY, Tree.class))); + + register(StringLiteralTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(CONTENT, tree.content()) + .add(VALUE, tree.value()), + + (ctx, json) -> new StringLiteralTreeImpl( + ctx.metaData(json), + ctx.fieldToString(json, VALUE), + ctx.fieldToString(json, CONTENT))); + + register(ThrowTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(KEYWORD, ctx.toJson(tree.keyword())) + .add(BODY, ctx.toJson(tree.body())), + + (ctx, json) -> new ThrowTreeImpl( + ctx.metaData(json), + ctx.fieldToToken(json, KEYWORD), + ctx.fieldToNullableObject(json, BODY, Tree.class))); + + register(TopLevelTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(DECLARATIONS, ctx.toJsonArray(tree.declarations())) + .add(FIRST_CPD_TOKEN, ctx.toJson(tree.firstCpdToken())), + + (ctx, json) -> { + List declarations = ctx.fieldToObjectList(json, DECLARATIONS, Tree.class); + Token firstCpdToken = ctx.fieldToNullableToken(json, FIRST_CPD_TOKEN); + TreeMetaData metaData = ctx.metaData(json); + return new TopLevelTreeImpl(metaData, declarations, metaData.commentsInside(), firstCpdToken); + }); + + register(UnaryExpressionTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(OPERATOR, ctx.toJson(tree.operator())) + .add(OPERAND, ctx.toJson(tree.operand())), + + (ctx, json) -> new UnaryExpressionTreeImpl( + ctx.metaData(json), + ctx.fieldToEnum(json, OPERATOR, UnaryExpressionTree.Operator.class), + ctx.fieldToObject(json, OPERAND, Tree.class))); + + register(VariableDeclarationTreeImpl.class, + + (ctx, tree) -> ctx.newTypedObject(tree) + .add(IDENTIFIER, ctx.toJson(tree.identifier())) + .add(TYPE, ctx.toJson(tree.type())) + .add(INITIALIZER, ctx.toJson(tree.initializer())) + .add(IS_VAL, tree.isVal()), + + (ctx, json) -> new VariableDeclarationTreeImpl( + ctx.metaData(json), + ctx.fieldToObject(json, IDENTIFIER, IdentifierTree.class), + ctx.fieldToNullableObject(json, TYPE, Tree.class), + ctx.fieldToNullableObject(json, INITIALIZER, Tree.class), + json.getBoolean(IS_VAL, false))); + + } + + private JsonTreeConverter() { + } + + private static void register(Class treeClass, Serialize treeToJson, Deserialize jsonToTree) { + String jsonType = treeClass.getSimpleName().replaceFirst("(TreeImpl|Impl)$", ""); + POLYMORPHIC_CONVERTER.register(treeClass, jsonType, treeToJson, jsonToTree); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/PolymorphicConverter.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/PolymorphicConverter.java new file mode 100644 index 00000000..e195046f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/PolymorphicConverter.java @@ -0,0 +1,80 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonValue; +import java.util.HashMap; +import java.util.function.BiFunction; +import javax.annotation.Nullable; + +public class PolymorphicConverter { + + @FunctionalInterface + public interface Serialize extends BiFunction { + } + + @FunctionalInterface + public interface Deserialize extends BiFunction { + } + + private final HashMap, Serialize> toJsonConverter = new HashMap<>(); + private final HashMap> fromJsonConverter = new HashMap<>(); + private final HashMap, String> jsonTypeByJavaClass = new HashMap<>(); + + public void register(Class treeClass, String jsonType, Serialize treeToJson, Deserialize jsonToTree) { + toJsonConverter.put(treeClass, treeToJson); + fromJsonConverter.put(jsonType, jsonToTree); + jsonTypeByJavaClass.put(treeClass, jsonType); + } + + public String getJsonType(Object object) { + return jsonTypeByJavaClass.get(object.getClass()); + } + + public JsonValue toJson(SerializationContext ctx, @Nullable T object) { + if (object == null) { + return Json.NULL; + } + Class objectClass = object.getClass(); + Serialize converter = (Serialize) toJsonConverter.get(objectClass); + if (converter == null) { + throw new IllegalStateException("Unsupported tree class: " + objectClass.getName()); + } + return converter.apply(ctx, object); + } + + public T fromJson(DeserializationContext ctx, String jsonType, JsonObject json, String memberName, Class expectedClass) { + ctx.pushPath(jsonType); + Deserialize converter = fromJsonConverter.get(jsonType); + if (converter == null) { + throw ctx.newIllegalMemberException("Invalid '@type' value", jsonType); + } + Object object = converter.apply(ctx, json); + if (!expectedClass.isInstance(object)) { + throw ctx.newIllegalMemberException("Unexpected '" + object.getClass().getName() + "' type for member '" + memberName + "'" + + " instead of '" + expectedClass.getName() + "'", json); + } + ctx.popPath(); + return expectedClass.cast(object); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/RangeConverter.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/RangeConverter.java new file mode 100644 index 00000000..ee0cf48e --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/RangeConverter.java @@ -0,0 +1,114 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import java.util.NoSuchElementException; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.TreeMetaDataProvider; + +public final class RangeConverter { + + private RangeConverter() { + } + + @Nullable + public static String format(@Nullable TextRange range) { + if (range == null) { + return null; + } + TextPointer start = range.start(); + TextPointer end = range.end(); + + String endLine = start.line() == end.line() ? "" : Integer.toString(end.line()); + return start.line() + ":" + start.lineOffset() + ":" + endLine + ":" + end.lineOffset(); + } + + @Nullable + public static TextRange parse(@Nullable String value) { + if (value == null) { + return null; + } + String[] values = value.split(":", 4); + if (values.length != 4) { + throw new IllegalArgumentException("Invalid TextRange '" + value + "'"); + } + int startLine = Integer.parseInt(values[0]); + int startLineOffset = Integer.parseInt(values[1]); + int endLine = values[2].isEmpty() ? startLine : Integer.parseInt(values[2]); + int endLineOffset = Integer.parseInt(values[3]); + return new TextRangeImpl(startLine, startLineOffset, endLine, endLineOffset); + } + + @Nullable + public static String tokenReference(@Nullable Token token) { + if (token == null) { + return null; + } + return format(token.textRange()); + } + + @Nullable + public static Token resolveToken(TreeMetaDataProvider metaDataProvider, @Nullable String tokenReference) { + TextRange range = parse(tokenReference); + if (range == null) { + return null; + } + return metaDataProvider.firstToken(range) + .orElseThrow(() -> new NoSuchElementException("Token not found: " + tokenReference)); + } + + public static String metaDataReference(Tree tree) { + return format(tree.metaData().textRange()); + } + + public static TreeMetaData resolveMetaData(TreeMetaDataProvider metaDataProvider, String metaDataReference) { + return metaDataProvider.metaData(parse(metaDataReference)); + } + + @Nullable + public static String treeReference(@Nullable Tree tree) { + if (tree == null) { + return null; + } + return format(tree.metaData().textRange()); + } + + @Nullable + public static T resolveNullableTree(Tree parent, @Nullable String treeReference, Class childClass) { + if (treeReference == null) { + return null; + } + TextRange range = parse(treeReference); + return Stream.concat(Stream.of(parent), parent.descendants()) + .filter(child -> child.textRange().equals(range)) + .filter(childClass::isInstance) + .map(childClass::cast) + .findFirst() + .orElse(null); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/SerializationContext.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/SerializationContext.java new file mode 100644 index 00000000..590be236 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/SerializationContext.java @@ -0,0 +1,86 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonArray; +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonValue; +import java.util.List; +import java.util.function.BiFunction; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; + +public class SerializationContext { + + public static final String TYPE_ATTRIBUTE = "@type"; + + private final PolymorphicConverter polymorphicConverter; + + public SerializationContext(PolymorphicConverter polymorphicConverter) { + this.polymorphicConverter = polymorphicConverter; + } + + public JsonObject newTypedObject(Tree tree) { + String jsonType = polymorphicConverter.getJsonType(tree); + if (jsonType == null) { + throw new IllegalStateException("Unsupported implementation class: " + tree.getClass().getName()); + } + return Json.object() + .add(TYPE_ATTRIBUTE, jsonType) + .add("metaData", RangeConverter.metaDataReference(tree)); + } + + public JsonValue toJson(@Nullable T object) { + return polymorphicConverter.toJson(this, object); + } + + public JsonValue toJson(@Nullable Token token) { + return Json.value(RangeConverter.tokenReference(token)); + } + + public JsonValue toJson(Enum entry) { + return Json.value(entry.name()); + } + + public JsonValue toJson(NativeKind kind) { + return Json.value(StringNativeKind.toString(kind)); + } + + public JsonValue toJson(@Nullable TextRange range) { + return Json.value(RangeConverter.format(range)); + } + + public JsonArray toJsonArray(List nodes) { + JsonArray array = Json.array(); + nodes.stream().map(this::toJson).forEach(array::add); + return array; + } + + public JsonArray toJsonArray(List nodes, BiFunction converter) { + JsonArray array = Json.array(); + nodes.stream().map(node -> converter.apply(this, node)).forEach(array::add); + return array; + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/StringNativeKind.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/StringNativeKind.java new file mode 100644 index 00000000..58a3dba6 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/StringNativeKind.java @@ -0,0 +1,69 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import javax.annotation.Nullable; +import org.sonarsource.slang.api.NativeKind; + +public class StringNativeKind implements NativeKind { + + private final String kind; + + public StringNativeKind(String kind) { + this.kind = kind; + } + + @Nullable + public static NativeKind of(@Nullable String value) { + if (value == null) { + return null; + } + return new StringNativeKind(value); + } + + @Nullable + public static String toString(@Nullable NativeKind nativeKind) { + if (nativeKind == null) { + return null; + } + return nativeKind.toString(); + } + + @Override + public String toString() { + return kind; + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (other == null || getClass() != other.getClass()) { + return false; + } + return kind.equals(((StringNativeKind) other).kind); + } + + @Override + public int hashCode() { + return kind.hashCode(); + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/package-info.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/package-info.java new file mode 100644 index 00000000..b476b0bb --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/conversion/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.persistence.conversion; diff --git a/slang-api/src/main/java/org/sonarsource/slang/persistence/package-info.java b/slang-api/src/main/java/org/sonarsource/slang/persistence/package-info.java new file mode 100644 index 00000000..1c6e7984 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/persistence/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.persistence; diff --git a/slang-api/src/main/java/org/sonarsource/slang/utils/LogArg.java b/slang-api/src/main/java/org/sonarsource/slang/utils/LogArg.java new file mode 100644 index 00000000..64796cc7 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/utils/LogArg.java @@ -0,0 +1,48 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.utils; + +import java.util.function.Supplier; + +/** + * slf4j does not support lambda argument, so this object wrap the lambda into an object + * that invoke the lambda when toString is called by the logger. + */ +public class LogArg { + + private final Supplier supplier; + + public LogArg(Supplier supplier) { + this.supplier = supplier; + } + + /** + * wrap a lambda that will only be called by the logger when the toString is called. + */ + public static Object lazyArg(Supplier supplier) { + return new LogArg(supplier); + } + + @Override + public String toString() { + return supplier.get(); + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/utils/SyntacticEquivalence.java b/slang-api/src/main/java/org/sonarsource/slang/utils/SyntacticEquivalence.java new file mode 100644 index 00000000..e55544c6 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/utils/SyntacticEquivalence.java @@ -0,0 +1,153 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.utils; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.visitors.TreePrinter; + +public class SyntacticEquivalence { + + private SyntacticEquivalence() { + } + + public static boolean areEquivalent(@Nullable List first, @Nullable List second) { + if (first == second) { + return true; + } + + if (first == null || second == null || first.size() != second.size()) { + return false; + } + + for (int i = 0; i < first.size(); i++) { + if (!areEquivalent(first.get(i), second.get(i))) { + return false; + } + } + + return true; + } + + public static boolean areEquivalent(@Nullable Tree first, @Nullable Tree second) { + if (first == second) { + return true; + } + + if (first == null || second == null || !first.getClass().equals(second.getClass())) { + return false; + } + + if (first instanceof IdentifierTree) { + return getUniqueIdentifier((IdentifierTree) first).equals(getUniqueIdentifier((IdentifierTree) second)); + } else if (first instanceof LiteralTree) { + return ((LiteralTree) first).value().equals(((LiteralTree) second).value()); + } else if (hasDifferentFields(first, second)) { + return false; + } else if (first instanceof NativeTree && first.children().isEmpty()) { + return areEquivalentTokenText(first.metaData().tokens(), second.metaData().tokens()); + } + return areEquivalent(first.children(), second.children()); + } + + public static String getUniqueIdentifier(IdentifierTree identifier) { + return identifier.identifier(); + } + + private static boolean areEquivalentTokenText(List firstList, List secondList) { + if (firstList.size() != secondList.size()) { + return false; + } + for (int i = 0; i < firstList.size(); i++) { + if (!firstList.get(i).text().equals(secondList.get(i).text())) { + return false; + } + } + return true; + } + + private static boolean hasDifferentFields(Tree first, Tree second) { + boolean nativeTreeCheck = (first instanceof NativeTree) && (!((NativeTree) first).nativeKind().equals(((NativeTree) second).nativeKind())); + boolean unaryTreeCheck = (first instanceof UnaryExpressionTree) && ((UnaryExpressionTree) first).operator() != ((UnaryExpressionTree) second).operator(); + boolean binaryTreeCheck = (first instanceof BinaryExpressionTree) && (((BinaryExpressionTree) first).operator() != ((BinaryExpressionTree) second).operator()); + boolean assignTreeCheck = (first instanceof AssignmentExpressionTree) && (((AssignmentExpressionTree) first).operator() != ((AssignmentExpressionTree) second).operator()); + boolean vardeclTreeCheck = (first instanceof VariableDeclarationTree) && (((VariableDeclarationTree) first).isVal() != ((VariableDeclarationTree) second).isVal()); + boolean loopTreeCheck = (first instanceof LoopTree) + && ((((LoopTree) first).kind() != ((LoopTree) second).kind()) || !(((LoopTree) first).keyword().text().equals(((LoopTree) second).keyword().text()))); + boolean modifierTreeCheck = (first instanceof ModifierTree) && (((ModifierTree) first).kind() != ((ModifierTree) second).kind()); + boolean jumpTreeCheck = (first instanceof JumpTree) && (((JumpTree) first).kind() != ((JumpTree) second).kind()); + return nativeTreeCheck || unaryTreeCheck || binaryTreeCheck || assignTreeCheck || vardeclTreeCheck || loopTreeCheck || modifierTreeCheck || jumpTreeCheck; + } + + public static List> findDuplicatedGroups(List list) { + return list.stream() + .collect(Collectors.groupingBy(ComparableTree::new, LinkedHashMap::new, Collectors.toList())) + .values().stream() + .filter(group -> group.size() > 1) + .collect(Collectors.toList()); + } + + static class ComparableTree { + + private final Tree tree; + private final int hash; + + ComparableTree(Tree tree) { + this.tree = tree; + hash = computeHash(tree); + } + + private static int computeHash(@Nullable Tree tree) { + if (tree == null) { + return 0; + } + return TreePrinter.tree2string(tree).hashCode(); + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof ComparableTree)) { + return false; + } + ComparableTree that = (ComparableTree) other; + return hash == that.hash && areEquivalent(tree, ((ComparableTree) other).tree); + } + + @Override + public int hashCode() { + return hash; + } + + } +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/utils/package-info.java b/slang-api/src/main/java/org/sonarsource/slang/utils/package-info.java new file mode 100644 index 00000000..26f88550 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/utils/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.utils; diff --git a/slang-api/src/main/java/org/sonarsource/slang/visitors/TreeContext.java b/slang-api/src/main/java/org/sonarsource/slang/visitors/TreeContext.java new file mode 100644 index 00000000..5c92f14b --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/visitors/TreeContext.java @@ -0,0 +1,56 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.visitors; + +import org.sonarsource.slang.api.Tree; +import java.util.ArrayDeque; +import java.util.Deque; + +public class TreeContext { + + private final Deque ancestors; + private Tree current; + + public TreeContext() { + ancestors = new ArrayDeque<>(); + } + + public Deque ancestors() { + return ancestors; + } + + protected void before(Tree root) { + ancestors.clear(); + } + + public void enter(Tree node) { + if (current != null) { + ancestors.push(current); + } + current = node; + } + + public void leave(Tree node) { + if (!ancestors.isEmpty()) { + current = ancestors.pop(); + } + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/visitors/TreePrinter.java b/slang-api/src/main/java/org/sonarsource/slang/visitors/TreePrinter.java new file mode 100644 index 00000000..d2103774 --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/visitors/TreePrinter.java @@ -0,0 +1,222 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.visitors; + +import java.util.ArrayList; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +public class TreePrinter { + + private TreePrinter() { + } + + public static String tree2string(List trees) { + return trees.stream().map(TreePrinter::tree2string).collect(Collectors.joining("\n")); + } + + public static String tree2string(Tree tree) { + StringBuilder sb = new StringBuilder(); + TreeVisitor visitor = new TreeVisitor<>(); + visitor.register(Tree.class, (ctx, t) -> { + IntStream.range(0, ctx.ancestors().size()).forEach(i -> sb.append(" ")); + sb.append(t.getClass().getSimpleName()); + if (t instanceof BinaryExpressionTree) { + sb.append(" ").append(((BinaryExpressionTree) t).operator().name()); + } else if (t instanceof AssignmentExpressionTree) { + sb.append(" ").append(((AssignmentExpressionTree) t).operator().name()); + } else if (t instanceof LiteralTree) { + sb.append(" ").append(((LiteralTree) t).value()); + } else if (t instanceof IdentifierTree) { + sb.append(" ").append(((IdentifierTree) t).name()); + } else if (t instanceof NativeTree) { + sb.append(" ").append(((NativeTree) t).nativeKind()); + } else if (t instanceof ModifierTree) { + sb.append(" ").append(((ModifierTree) t).kind()); + } + sb.append("\n"); + }); + visitor.scan(new TreeContext(), tree); + return sb.toString(); + } + + public static String table(Tree tree) { + Table table = new Table("AST node class", "first…last tokens", "line:col"); + addAstNode(table, tree, 0); + return table.toString(); + } + + private static void addAstNode(Table table, Tree node, int indentSize) { + String indent = repeat(' ', indentSize); + boolean hasChildren = !node.children().isEmpty(); + table.add(indent + kind(node) + (hasChildren ? " {" : ""), + firstToLastTokens(node.metaData().tokens()), + toLineColumn(node)); + for (Tree child : node.children()) { + addAstNode(table, child, indentSize + 2); + } + if (hasChildren) { + table.add(indent + "}", "", ""); + } + } + + private static String firstToLastTokens(List tokens) { + if (tokens.isEmpty()) { + return ""; + } else { + String firstToken = escapeWhiteSpace(tokens.get(0).text()); + String lastToken = escapeWhiteSpace(tokens.get(tokens.size() - 1).text()); + if (tokens.size() == 1) { + return truncate(firstToken, 23); + } else { + return truncateFromLeft(firstToken, 10) + + " … " + truncateFromRight(lastToken, 10); + } + } + } + + private static String truncate(String text, int maxSize) { + if (text.length() > maxSize) { + return truncateFromLeft(text, maxSize / 2) + + "…" + truncateFromRight(text, maxSize - (maxSize / 2) - 1); + } else { + return text; + } + } + + private static String truncateFromLeft(String text, int maxSize) { + return text.length() > maxSize ? text.substring(0, maxSize) : text; + } + + private static String truncateFromRight(String text, int maxSize) { + return text.length() > maxSize ? text.substring(text.length() - maxSize) : text; + } + + private static String escapeWhiteSpace(String text) { + return text.replace("\\", "\\\\") + .replace("\n", "\\n") + .replace("\r", "\\r") + .replace("\t", "\\t"); + } + + public static String kind(Tree node) { + if (node instanceof NativeTree) { + return "?" + ((NativeTree) node).nativeKind().toString() + "?"; + } else { + return node.getClass().getSimpleName().replaceFirst("Impl$", ""); + } + } + + private static String toLineColumn(Tree node) { + return toLineColumn(node.textRange()); + } + + private static String toLineColumn(TextRange range) { + return toLineColumn(range.start()) + " … " + toLineColumn(range.end()); + } + + private static String toLineColumn(TextPointer pointer) { + return pointer.line() + ":" + (pointer.lineOffset() + 1); + } + + private static String repeat(char filler, int count) { + return new String(new char[count]).replace('\0', filler); + } + + public static class Table { + + private final int colCount; + private final String[] columnNames; + private final int[] colWidths; + private final List rows; + + public Table(String... columnNames) { + colCount = columnNames.length; + colWidths = new int[colCount]; + this.columnNames = columnNames; + for (int col = 0; col < colCount; col++) { + colWidths[col] = this.columnNames[col].length(); + } + rows = new ArrayList<>(); + } + + public void add(Object... columnValues) { + if (columnValues.length != colCount) { + throw new IllegalArgumentException("columnValues.length (" + columnNames.length + ") must be " + colCount); + } + String[] row = new String[colCount]; + for (int col = 0; col < colCount; col++) { + row[col] = String.valueOf(columnValues[col]); + if (colWidths[col] < row[col].length()) { + colWidths[col] = row[col].length(); + } + } + rows.add(row); + } + + @Override + public String toString() { + StringBuilder out = new StringBuilder(); + for (int col = 0; col < colCount; col++) { + appendCol(out, columnNames[col], col, ' '); + } + out.append('\n'); + for (int col = 0; col < colCount; col++) { + appendCol(out, "", col, '-'); + } + for (String[] row : rows) { + out.append('\n'); + for (int col = 0; col < colCount; col++) { + appendCol(out, row[col], col, ' '); + } + } + return out.toString(); + } + + private void appendCol(StringBuilder out, String value, int col, char filler) { + int start = out.length(); + out.append(value); + fill(out, filler, start + colWidths[col]); + if (col + 1 < colCount) { + out.append(filler).append('|').append(filler); + } + } + + private static void fill(StringBuilder out, char filler, int endExcluded) { + while (out.length() < endExcluded) { + out.append(filler); + } + } + + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/visitors/TreeVisitor.java b/slang-api/src/main/java/org/sonarsource/slang/visitors/TreeVisitor.java new file mode 100644 index 00000000..b16f315f --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/visitors/TreeVisitor.java @@ -0,0 +1,93 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.visitors; + +import org.sonarsource.slang.api.Tree; +import java.util.ArrayList; +import java.util.List; +import java.util.function.BiConsumer; +import javax.annotation.Nullable; + +public class TreeVisitor { + + private List> consumers; + + public TreeVisitor() { + consumers = null; + } + + public void scan(C ctx, @Nullable Tree root) { + if (root != null) { + ctx.before(root); + before(ctx, root); + visit(ctx, root); + after(ctx, root); + } + } + + private void visit(C ctx, @Nullable Tree node) { + if (node != null) { + ctx.enter(node); + if (consumers != null) { + for (ConsumerFilter consumer : consumers) { + consumer.accept(ctx, node); + } + } + node.children().forEach(child -> visit(ctx, child)); + ctx.leave(node); + } + } + + protected void before(C ctx, Tree root) { + // default behaviour is to do nothing + } + + protected void after(C ctx, Tree root) { + // default behaviour is to do nothing + } + + public TreeVisitor register(Class cls, BiConsumer visitor) { + if (consumers == null) { + consumers = new ArrayList<>(); + } + consumers.add(new ConsumerFilter<>(cls, visitor)); + return this; + } + + private static class ConsumerFilter { + + private final Class cls; + + private final BiConsumer delegate; + + private ConsumerFilter(Class cls, BiConsumer delegate) { + this.cls = cls; + this.delegate = delegate; + } + + private void accept(C ctx, Tree node) { + if (cls.isAssignableFrom(node.getClass())) { + delegate.accept(ctx, cls.cast(node)); + } + } + + } + +} diff --git a/slang-api/src/main/java/org/sonarsource/slang/visitors/package-info.java b/slang-api/src/main/java/org/sonarsource/slang/visitors/package-info.java new file mode 100644 index 00000000..ee024adc --- /dev/null +++ b/slang-api/src/main/java/org/sonarsource/slang/visitors/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.visitors; diff --git a/slang-api/src/test/java/org/sonarsource/slang/api/ASTConverterTest.java b/slang-api/src/test/java/org/sonarsource/slang/api/ASTConverterTest.java new file mode 100644 index 00000000..9835af40 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/api/ASTConverterTest.java @@ -0,0 +1,42 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.impl.IdentifierTreeImpl; + +import static org.junit.jupiter.api.Assertions.assertSame; + +class ASTConverterTest { + + private static final ASTConverter DUMMY_CONVERTER = new ASTConverter() { + private final Tree SIMPLE_TREE = new IdentifierTreeImpl(null, "name"); + @Override + public Tree parse(String content) { + return SIMPLE_TREE; + } + }; + + @Test + void parse_with_file_has_no_effect_by_default() { + assertSame(DUMMY_CONVERTER.parse(""), DUMMY_CONVERTER.parse("", "file name")); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/api/TextRangeTest.java b/slang-api/src/test/java/org/sonarsource/slang/api/TextRangeTest.java new file mode 100644 index 00000000..8fc0a52b --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/api/TextRangeTest.java @@ -0,0 +1,37 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import org.sonarsource.slang.impl.TextRangeImpl; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TextRangeTest { + + @Test + void isInside() { + assertThat(new TextRangeImpl(1, 1, 1, 9).isInside(new TextRangeImpl(1, 1, 1, 5))).isFalse(); + assertThat(new TextRangeImpl(1, 1, 1, 9).isInside(new TextRangeImpl(1, 1, 1, 9))).isTrue(); + assertThat(new TextRangeImpl(1, 5, 1, 9).isInside(new TextRangeImpl(1, 1, 1, 9))).isTrue(); + assertThat(new TextRangeImpl(1, 5, 1, 9).isInside(new TextRangeImpl(1, 6, 1, 9))).isFalse(); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/api/TreeTest.java b/slang-api/src/test/java/org/sonarsource/slang/api/TreeTest.java new file mode 100644 index 00000000..b24b2115 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/api/TreeTest.java @@ -0,0 +1,64 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.api; + +import java.util.Arrays; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.EQUAL_TO; +import static org.sonarsource.slang.utils.TreeCreationUtils.assignment; +import static org.sonarsource.slang.utils.TreeCreationUtils.binary; +import static org.sonarsource.slang.utils.TreeCreationUtils.block; +import static org.sonarsource.slang.utils.TreeCreationUtils.identifier; +import static org.sonarsource.slang.utils.TreeCreationUtils.integerLiteral; +import static org.sonarsource.slang.utils.TreeCreationUtils.simpleFunction; +import static org.sonarsource.slang.utils.TreeCreationUtils.simpleNative; +import static org.sonarsource.slang.utils.TreeCreationUtils.topLevel; +import static org.assertj.core.api.Assertions.assertThat; + +class TreeTest { + + private static final NativeKind SIMPLE_KIND = new NativeKind() { + }; + + @Test + void test() { + Tree x = identifier("x"); + Tree y = identifier("y"); + Tree z = identifier("z"); + Tree int1 = integerLiteral("1"); + Tree xEqualTo1 = binary(EQUAL_TO, x, int1); + Tree yEqualsXEqualTo1 = assignment(y, xEqualTo1); + IdentifierTree functionName = identifier("x"); + BlockTree functionBody = block(Arrays.asList(xEqualTo1, yEqualsXEqualTo1)); + Tree function = simpleFunction(functionName, functionBody); + Tree nativeTree = simpleNative(SIMPLE_KIND, Collections.singletonList(z)); + Tree topLevelTree = topLevel(Arrays.asList(function, nativeTree)); + + assertThat(topLevelTree.descendants()) + .containsExactly(function, functionName, functionBody, xEqualTo1, x, int1, yEqualsXEqualTo1, y, xEqualTo1, x, int1, nativeTree, z); + assertThat(function.descendants()) + .containsExactly(functionName, functionBody, xEqualTo1, x, int1, yEqualsXEqualTo1, y, xEqualTo1, x, int1); + assertThat(yEqualsXEqualTo1.descendants()) + .containsExactly(y, xEqualTo1, x, int1); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/checks/api/CheckContextTest.java b/slang-api/src/test/java/org/sonarsource/slang/checks/api/CheckContextTest.java new file mode 100644 index 00000000..3aa088b4 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/checks/api/CheckContextTest.java @@ -0,0 +1,93 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.api; + +import java.util.List; +import javax.annotation.Nullable; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.visitors.TreeContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class CheckContextTest { + + @Test + void parent_default_method() { + Tree a = mock(Tree.class); + Tree b = mock(Tree.class); + Tree c = mock(Tree.class); + + CheckContextToTestDefaultMethod context = new CheckContextToTestDefaultMethod(); + assertThat(context.parent()).isNull(); + + context.enter(a); + assertThat(context.parent()).isNull(); + + context.enter(b); + assertThat(context.parent()).isSameAs(a); + + context.enter(c); + assertThat(context.parent()).isSameAs(b); + + context.leave(c); + assertThat(context.parent()).isSameAs(a); + + context.leave(b); + assertThat(context.parent()).isNull(); + } + + private static class CheckContextToTestDefaultMethod extends TreeContext implements CheckContext { + + public String filename() { + return null; + } + + public String fileContent() { + return null; + } + + public void reportIssue(TextRange textRange, String message) { + } + + public void reportIssue(HasTextRange toHighlight, String message) { + } + + public void reportIssue(HasTextRange toHighlight, String message, SecondaryLocation secondaryLocation) { + } + + public void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations) { + } + + public void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations, @Nullable Double gap) { + } + + public void reportFileIssue(String message) { + } + + public void reportFileIssue(String message, @Nullable Double gap) { + } + + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/checks/api/SecondaryLocationTest.java b/slang-api/src/test/java/org/sonarsource/slang/checks/api/SecondaryLocationTest.java new file mode 100644 index 00000000..92d19e5e --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/checks/api/SecondaryLocationTest.java @@ -0,0 +1,58 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.api; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.persistence.JsonTree; + +import static org.assertj.core.api.Assertions.assertThat; + +class SecondaryLocationTest { + + private static final Tree IDENTIFIER = JsonTree.fromJson("" + + "{\n" + + " \"treeMetaData\": {\"tokens\": [{\"textRange\": \"1:0:1:3\", \"text\": \"foo\", \"type\": \"OTHER\"}]},\n" + + " \"tree\": {\"@type\": \"Identifier\", \"metaData\": \"1:0:1:3\", \"name\": \"foo\"}\n" + + "}"); + + @Test + void constructor_with_tree() { + SecondaryLocation location = new SecondaryLocation(IDENTIFIER); + assertThat(location.textRange).isEqualTo(new TextRangeImpl(1, 0, 1, 3)); + assertThat(location.message).isNull(); + } + + @Test + void constructor_with_tree_and_message() { + SecondaryLocation location = new SecondaryLocation(IDENTIFIER, "because"); + assertThat(location.textRange).isEqualTo(new TextRangeImpl(1, 0, 1, 3)); + assertThat(location.message).isEqualTo("because"); + } + + @Test + void constructor_with_text_range_and_message() { + SecondaryLocation location = new SecondaryLocation(IDENTIFIER.textRange(), "because"); + assertThat(location.textRange).isEqualTo(new TextRangeImpl(1, 0, 1, 3)); + assertThat(location.message).isEqualTo("because"); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/CatchTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/CatchTreeImplTest.java new file mode 100644 index 00000000..2d7b2ae6 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/CatchTreeImplTest.java @@ -0,0 +1,55 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.impl.TextRanges.range; +import static org.assertj.core.api.Assertions.assertThat; + +class CatchTreeImplTest { + + @Test + void test() { + TreeMetaData meta = null; + Token keyword = new TokenImpl(range(1, 2, 3, 4), "catch", Token.Type.KEYWORD); + ParameterTree parameter = new ParameterTreeImpl(meta, new IdentifierTreeImpl(meta,"e"), null); + Tree lhs = new IdentifierTreeImpl(meta, "x"); + Tree one = new LiteralTreeImpl(meta, "1"); + Tree assignmentExpressionTree = + new AssignmentExpressionTreeImpl(meta, AssignmentExpressionTree.Operator.EQUAL, lhs, one); + CatchTreeImpl catchWithIdentifier = new CatchTreeImpl(meta, parameter, assignmentExpressionTree, keyword); + CatchTreeImpl catchWithoutIdentifier = new CatchTreeImpl(meta, null, assignmentExpressionTree, keyword); + + assertThat(catchWithIdentifier.children()).containsExactly(parameter, assignmentExpressionTree); + assertThat(catchWithIdentifier.catchParameter()).isEqualTo(parameter); + assertThat(catchWithIdentifier.catchBlock()).isEqualTo(assignmentExpressionTree); + assertThat(catchWithIdentifier.keyword()).isEqualTo(keyword); + + assertThat(catchWithoutIdentifier.children()).containsExactly(assignmentExpressionTree); + assertThat(catchWithoutIdentifier.catchParameter()).isNull(); + assertThat(catchWithoutIdentifier.catchBlock()).isEqualTo(assignmentExpressionTree); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/ClassDeclarationTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/ClassDeclarationTreeImplTest.java new file mode 100644 index 00000000..67de792e --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/ClassDeclarationTreeImplTest.java @@ -0,0 +1,48 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.assertj.core.api.Assertions.assertThat; + +class ClassDeclarationTreeImplTest { + + private class ClassNativeKind implements NativeKind {} + + @Test + void test() { + TreeMetaData meta = null; + IdentifierTree className = new IdentifierTreeImpl(meta, "MyClass"); + Tree classDecl = new NativeTreeImpl(meta, new ClassNativeKind(), Collections.singletonList(className)); + ClassDeclarationTree tree = new ClassDeclarationTreeImpl(meta, className, classDecl); + assertThat(tree.children()).hasSize(1); + assertThat(areEquivalent(tree.children().get(0), classDecl)).isTrue(); + assertThat(areEquivalent(tree.identifier(), className)).isTrue(); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/ExceptionHandlingTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/ExceptionHandlingTreeImplTest.java new file mode 100644 index 00000000..d4f3cba3 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/ExceptionHandlingTreeImplTest.java @@ -0,0 +1,64 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.CatchTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class ExceptionHandlingTreeImplTest { + + @Test + void test() { + TreeMetaData meta = null; + ParameterTree parameter = new ParameterTreeImpl(meta, new IdentifierTreeImpl(meta, "e"), null); + Tree lhs = new IdentifierTreeImpl(meta, "x"); + Tree one = new LiteralTreeImpl(meta, "1"); + Tree assignmentExpressionTree = + new AssignmentExpressionTreeImpl(meta, AssignmentExpressionTree.Operator.EQUAL, lhs, one); + CatchTreeImpl catchWithIdentifier = new CatchTreeImpl(meta, parameter, assignmentExpressionTree, null); + CatchTreeImpl catchWithoutIdentifier = new CatchTreeImpl(meta, null, assignmentExpressionTree, null); + TokenImpl tryToken = new TokenImpl(new TextRangeImpl(1, 0, 1, 3), "try", Token.Type.KEYWORD); + + Tree emptyTry = new BlockTreeImpl(meta, Collections.emptyList()); + + List catchTreeList = Arrays.asList(catchWithIdentifier, catchWithoutIdentifier); + + Tree emptyFinally = new BlockTreeImpl(meta, Collections.emptyList()); + + ExceptionHandlingTreeImpl exceptionHandlingTree = + new ExceptionHandlingTreeImpl(null, emptyTry, tryToken, catchTreeList, emptyFinally); + + assertThat(exceptionHandlingTree.children()).containsExactly(emptyTry, catchWithIdentifier, catchWithoutIdentifier, emptyFinally); + assertThat(exceptionHandlingTree.tryBlock()).isEqualTo(emptyTry); + assertThat(exceptionHandlingTree.catchBlocks()).containsExactly(catchWithIdentifier, catchWithoutIdentifier); + assertThat(exceptionHandlingTree.finallyBlock()).isEqualTo(emptyFinally); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/FunctionDeclarationTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/FunctionDeclarationTreeImplTest.java new file mode 100644 index 00000000..44ef578e --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/FunctionDeclarationTreeImplTest.java @@ -0,0 +1,125 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.api.ModifierTree.Kind.PUBLIC; +import static org.sonarsource.slang.impl.TextRanges.range; +import static org.sonarsource.slang.utils.TreeCreationUtils.identifier; +import static org.sonarsource.slang.utils.TreeCreationUtils.simpleNative; + +class FunctionDeclarationTreeImplTest { + private static final NativeKind SIMPLE_KIND = new NativeKind() { + }; + + @Test + void test() { + TreeMetaData meta = null; + List modifiers = Arrays.asList(new ModifierTreeImpl(meta, PUBLIC)); + Tree returnType = new IdentifierTreeImpl(meta, "int"); + IdentifierTree name = new IdentifierTreeImpl(meta, "foo"); + IdentifierTree paramName = new IdentifierTreeImpl(meta, "p1"); + ParameterTree param = new ParameterTreeImpl(meta, paramName, null); + List params = singletonList(param); + BlockTree body = new BlockTreeImpl(meta, emptyList()); + IdentifierTree child = identifier("GENERIC"); + NativeTree nativeChildren = simpleNative(SIMPLE_KIND, singletonList(child)); + + FunctionDeclarationTreeImpl tree = + new FunctionDeclarationTreeImpl(meta, modifiers, false, returnType, name, params, body, singletonList(nativeChildren)); + assertThat(tree.children()).hasSize(6); + assertThat(tree.children()).containsExactly(modifiers.get(0), returnType, name, param, body, nativeChildren); + assertThat(tree.modifiers()).isEqualTo(modifiers); + assertThat(tree.returnType()).isEqualTo(returnType); + assertThat(tree.name()).isEqualTo(name); + assertThat(tree.formalParameters()).isEqualTo(params); + assertThat(tree.body()).isEqualTo(body); + assertThat(tree.nativeChildren()).isEqualTo(singletonList(nativeChildren)); + assertThat(tree.isConstructor()).isFalse(); + + FunctionDeclarationTreeImpl lightweightConstructor = new FunctionDeclarationTreeImpl(meta, modifiers, true, null, null, emptyList(), null, emptyList()); + + assertThat(lightweightConstructor.children()).containsExactly(modifiers.get(0)); + assertThat(lightweightConstructor.nativeChildren()).isEmpty(); + assertThat(lightweightConstructor.isConstructor()).isTrue(); + } + + @Test + void rangeToHighlight_with_name() { + TreeMetaDataProvider metaDataProvider = new TreeMetaDataProvider(emptyList(), emptyList()); + TreeMetaData nameMetaData = metaDataProvider.metaData(range(1, 2, 3, 4)); + TreeMetaData bodyMetaData = metaDataProvider.metaData(range(5, 1, 5, 7)); + IdentifierTree name = new IdentifierTreeImpl(nameMetaData, "foo"); + BlockTree body = new BlockTreeImpl(bodyMetaData, emptyList()); + assertThat(new FunctionDeclarationTreeImpl(body.metaData(), emptyList(), false, null, name, emptyList(), body, emptyList()).rangeToHighlight()) + .isEqualTo(nameMetaData.textRange()); + } + + @Test + void rangeToHighlight_with_body_only() { + TreeMetaDataProvider metaDataProvider = new TreeMetaDataProvider(emptyList(), Arrays.asList( + new TokenImpl(range(5, 1, 17, 18), "{", Token.Type.OTHER), + new TokenImpl(range(5, 1, 19, 20), "}", Token.Type.OTHER))); + TreeMetaData bodyMetaData = metaDataProvider.metaData(range(5, 1, 17, 20)); + BlockTree body = new BlockTreeImpl(bodyMetaData, emptyList()); + assertThat(new FunctionDeclarationTreeImpl(body.metaData(), emptyList(), false, null, null, emptyList(), body, emptyList()).rangeToHighlight()) + .isEqualTo(body.metaData().textRange()); + } + + @Test + void rangeToHighlight_with_no_name_but_some_signature() { + TreeMetaDataProvider metaDataProvider = new TreeMetaDataProvider(emptyList(), Arrays.asList( + new TokenImpl(range(5, 1, 5, 10), "fun", Token.Type.KEYWORD), + new TokenImpl(range(5, 11, 5, 15), "foo", Token.Type.OTHER), + new TokenImpl(range(5, 17, 5, 18), "{", Token.Type.OTHER), + new TokenImpl(range(5, 19, 5, 20), "}", Token.Type.OTHER))); + TreeMetaData functionMetaData = metaDataProvider.metaData(range(5, 1, 5, 20)); + TreeMetaData bodyMetaData = metaDataProvider.metaData(range(5, 17, 5, 20)); + BlockTree body = new BlockTreeImpl(bodyMetaData, emptyList()); + assertThat(new FunctionDeclarationTreeImpl(functionMetaData, emptyList(), false, null, null, emptyList(), body, emptyList()).rangeToHighlight()) + .isEqualTo(range(5, 1, 5, 15)); + } + + @Test + void rangeToHighlight_with_no_name_no_body_but_some_signature() { + TreeMetaDataProvider metaDataProvider = new TreeMetaDataProvider(emptyList(), Arrays.asList( + new TokenImpl(range(5, 1, 5, 10), "fun", Token.Type.KEYWORD), + new TokenImpl(range(5, 11, 5, 12), "(", Token.Type.OTHER), + new TokenImpl(range(5, 13, 5, 14), ")", Token.Type.OTHER))); + TreeMetaData functionMetaData = metaDataProvider.metaData(range(5, 1, 5, 14)); + assertThat(new FunctionDeclarationTreeImpl(functionMetaData, emptyList(), false, null, null, emptyList(), null, emptyList()).rangeToHighlight()) + .isEqualTo(range(5, 1, 5, 14)); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/FunctionInvocationTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/FunctionInvocationTreeImplTest.java new file mode 100644 index 00000000..690a53fb --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/FunctionInvocationTreeImplTest.java @@ -0,0 +1,78 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.FunctionInvocationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +import static org.assertj.core.api.Assertions.assertThat; + +class FunctionInvocationTreeImplTest { + + @Test + void simple_function_invocation() { + TreeMetaData meta = null; + Tree identifierTree = new IdentifierTreeImpl(meta, "x"); + List args = new ArrayList<>(); + + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, identifierTree, args); + assertThat(tree.children()).containsExactly(identifierTree); + assertThat(tree.arguments()).isNotNull(); + assertThat(tree.arguments()).isEmpty(); + assertThat(tree.memberSelect()).isEqualTo(identifierTree); + } + + @Test + void function_invocation_with_arguments() { + TreeMetaData meta = null; + Tree identifierTree = new IdentifierTreeImpl(meta, "x"); + Tree arg1 = new IdentifierTreeImpl(meta, "x"); + Tree arg2 = new LiteralTreeImpl(meta, "x"); + List args = new ArrayList<>(); + args.add(arg1); + args.add(arg2); + + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, identifierTree, args); + assertThat(tree.children()).containsExactly(identifierTree, arg1, arg2); + assertThat(tree.arguments()).isNotNull(); + assertThat(tree.arguments()).hasSize(2); + assertThat(tree.arguments().get(0)).isEqualTo(arg1); + assertThat(tree.arguments().get(1)).isEqualTo(arg2); + assertThat(tree.memberSelect()).isEqualTo(identifierTree); + } + + @Test + void function_invocation_with_member_select() { + TreeMetaData meta = null; + IdentifierTree identifierTree = new IdentifierTreeImpl(meta, "y"); + Tree member = new IdentifierTreeImpl(meta, "x"); + Tree memberSelect = new MemberSelectTreeImpl(meta, member, identifierTree); + List args = new ArrayList<>(); + + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, memberSelect, args); + assertThat(tree.children()).containsExactly(memberSelect); + assertThat(tree.memberSelect()).isEqualTo(memberSelect); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/IfTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/IfTreeImplTest.java new file mode 100644 index 00000000..0d52e472 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/IfTreeImplTest.java @@ -0,0 +1,49 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class IfTreeImplTest { + + @Test + void test() { + TreeMetaData meta = null; + Tree condition = new LiteralTreeImpl(meta, "42"); + Tree thenBranch = new IdentifierTreeImpl(meta, "x"); + Tree elseBranch = new IdentifierTreeImpl(meta, "y"); + TokenImpl ifToken = new TokenImpl(new TextRangeImpl(1, 0, 1, 2), "if", Token.Type.KEYWORD); + TokenImpl elseToken = new TokenImpl(new TextRangeImpl(2, 0, 1, 4), "else", Token.Type.KEYWORD); + IfTreeImpl tree = new IfTreeImpl(meta, condition, thenBranch, elseBranch, ifToken, elseToken); + assertThat(tree.children()).containsExactly(condition, thenBranch, elseBranch); + assertThat(tree.condition()).isEqualTo(condition); + assertThat(tree.thenBranch()).isEqualTo(thenBranch); + assertThat(tree.elseBranch()).isEqualTo(elseBranch); + + assertThat(new IfTreeImpl(meta, condition, thenBranch, null, ifToken, null) + .children()).containsExactly(condition, thenBranch); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/ImportDeclarationTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/ImportDeclarationTreeImplTest.java new file mode 100644 index 00000000..8f0fa7c5 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/ImportDeclarationTreeImplTest.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Collections; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.ImportDeclarationTree; +import org.sonarsource.slang.api.Tree; + +import static org.sonarsource.slang.utils.TreeCreationUtils.identifier; + +class ImportDeclarationTreeImplTest { + + @Test + void test() { + Tree identifier = identifier("x"); + ImportDeclarationTree tree = new ImportDeclarationTreeImpl(null, Collections.singletonList(identifier)); + Assertions.assertThat(tree.children()).containsExactly(identifier); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/IntegerLiteralTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/IntegerLiteralTreeImplTest.java new file mode 100644 index 00000000..62c7547a --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/IntegerLiteralTreeImplTest.java @@ -0,0 +1,85 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.api.IntegerLiteralTree.Base.BINARY; +import static org.sonarsource.slang.api.IntegerLiteralTree.Base.DECIMAL; +import static org.sonarsource.slang.api.IntegerLiteralTree.Base.HEXADECIMAL; +import static org.sonarsource.slang.api.IntegerLiteralTree.Base.OCTAL; +import static org.sonarsource.slang.utils.TreeCreationUtils.integerLiteral; + +class IntegerLiteralTreeImplTest { + + @Test + void bases() { + assertThat(integerLiteral("0123").getBase()).isEqualTo(OCTAL); + assertThat(integerLiteral("0o123").getBase()).isEqualTo(OCTAL); + assertThat(integerLiteral("0O123").getBase()).isEqualTo(OCTAL); + + assertThat(integerLiteral("123").getBase()).isEqualTo(DECIMAL); + assertThat(integerLiteral("0d123").getBase()).isEqualTo(DECIMAL); + assertThat(integerLiteral("0D123").getBase()).isEqualTo(DECIMAL); + + assertThat(integerLiteral("0x123").getBase()).isEqualTo(HEXADECIMAL); + assertThat(integerLiteral("0X123").getBase()).isEqualTo(HEXADECIMAL); + + assertThat(integerLiteral("0b101").getBase()).isEqualTo(BINARY); + assertThat(integerLiteral("0B101").getBase()).isEqualTo(BINARY); + } + + @Test + void integerValues() { + assertThat(integerLiteral("0123").getIntegerValue().intValue()).isEqualTo(83); + assertThat(integerLiteral("0o123").getIntegerValue().intValue()).isEqualTo(83); + assertThat(integerLiteral("0O123").getIntegerValue().intValue()).isEqualTo(83); + assertThat(integerLiteral("0O00123").getIntegerValue().intValue()).isEqualTo(83); + + assertThat(integerLiteral("123").getIntegerValue().intValue()).isEqualTo(123); + assertThat(integerLiteral("0d123").getIntegerValue().intValue()).isEqualTo(123); + assertThat(integerLiteral("0D123").getIntegerValue().intValue()).isEqualTo(123); + + assertThat(integerLiteral("0x123").getIntegerValue().intValue()).isEqualTo(291); + assertThat(integerLiteral("0X123").getIntegerValue().intValue()).isEqualTo(291); + + assertThat(integerLiteral("0b101").getIntegerValue().intValue()).isEqualTo(5); + assertThat(integerLiteral("0B101").getIntegerValue().intValue()).isEqualTo(5); + } + + @Test + void numericPart() { + assertThat(integerLiteral("0123").getNumericPart()).isEqualTo("123"); + assertThat(integerLiteral("0o123").getNumericPart()).isEqualTo("123"); + assertThat(integerLiteral("0O123").getNumericPart()).isEqualTo("123"); + + assertThat(integerLiteral("123").getNumericPart()).isEqualTo("123"); + assertThat(integerLiteral("0d123").getNumericPart()).isEqualTo("123"); + assertThat(integerLiteral("0D123").getNumericPart()).isEqualTo("123"); + + assertThat(integerLiteral("0x123").getNumericPart()).isEqualTo("123"); + assertThat(integerLiteral("0X123").getNumericPart()).isEqualTo("123"); + + assertThat(integerLiteral("0b101").getNumericPart()).isEqualTo("101"); + assertThat(integerLiteral("0B101").getNumericPart()).isEqualTo("101"); + } + +} \ No newline at end of file diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/JumpTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/JumpTreeImplTest.java new file mode 100644 index 00000000..b4e25ef8 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/JumpTreeImplTest.java @@ -0,0 +1,87 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TreeMetaData; +import javax.annotation.Nullable; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.assertj.core.api.Assertions.assertThat; + +class JumpTreeImplTest { + + @Test + void test_break() { + JumpTree breakWithLabel = getJumpTree(JumpTree.JumpKind.BREAK, "foo"); + assertThat(breakWithLabel.children()).hasSize(1); + assertThat(breakWithLabel.label().name()).isEqualTo("foo"); + assertThat(breakWithLabel.kind()).isEqualTo(JumpTree.JumpKind.BREAK); + + JumpTree breakWithoutLabel = getJumpTree(JumpTree.JumpKind.BREAK, null); + assertThat(breakWithoutLabel.children()).isEmpty(); + assertThat(breakWithoutLabel.label()).isNull(); + assertThat(breakWithLabel.kind()).isEqualTo(JumpTree.JumpKind.BREAK); + } + + @Test + void test_continue() { + JumpTree continueWithLabel = getJumpTree(JumpTree.JumpKind.CONTINUE, "foo"); + assertThat(continueWithLabel.children()).hasSize(1); + assertThat(continueWithLabel.label().name()).isEqualTo("foo"); + assertThat(continueWithLabel.kind()).isEqualTo(JumpTree.JumpKind.CONTINUE); + + JumpTree continueWithoutLabel = getJumpTree(JumpTree.JumpKind.CONTINUE, null); + assertThat(continueWithoutLabel.children()).isEmpty(); + assertThat(continueWithoutLabel.label()).isNull(); + assertThat(continueWithoutLabel.kind()).isEqualTo(JumpTree.JumpKind.CONTINUE); + } + + @Test + void test_syntactic_equivalence() { + JumpTree jumpTreeBreak = getJumpTree(JumpTree.JumpKind.BREAK, "foo"); + JumpTree jumpTreeContinue = getJumpTree(JumpTree.JumpKind.CONTINUE, "foo"); + assertThat(areEquivalent(jumpTreeBreak, getJumpTree(JumpTree.JumpKind.BREAK, "foo"))).isTrue(); + assertThat(areEquivalent(jumpTreeContinue, getJumpTree(JumpTree.JumpKind.CONTINUE, "foo"))).isTrue(); + + assertThat(areEquivalent(jumpTreeBreak, jumpTreeContinue)).isFalse(); + + assertThat(areEquivalent(jumpTreeBreak, getJumpTree(JumpTree.JumpKind.BREAK, "bar"))).isFalse(); + assertThat(areEquivalent(jumpTreeContinue, getJumpTree(JumpTree.JumpKind.CONTINUE, "bar"))).isFalse(); + + assertThat(areEquivalent(jumpTreeBreak, getJumpTree(JumpTree.JumpKind.BREAK, null))).isFalse(); + assertThat(areEquivalent(jumpTreeContinue, getJumpTree(JumpTree.JumpKind.CONTINUE, null))).isFalse(); + } + + private static JumpTree getJumpTree(JumpTree.JumpKind kind, @Nullable String labelText) { + TreeMetaData meta = null; + String keywordText = kind == JumpTree.JumpKind.BREAK ? "break" : "continue"; + TokenImpl keyword = new TokenImpl(new TextRangeImpl(1, 0, 1, keywordText.length()), keywordText, Token.Type.KEYWORD); + IdentifierTree label = null; + if (labelText != null) { + label = new IdentifierTreeImpl(meta, labelText); + } + return new JumpTreeImpl(meta, keyword, kind, label); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/LoopTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/LoopTreeImplTest.java new file mode 100644 index 00000000..03b88b1a --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/LoopTreeImplTest.java @@ -0,0 +1,65 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.api.LoopTree.LoopKind.DOWHILE; +import static org.sonarsource.slang.api.LoopTree.LoopKind.FOR; +import static org.sonarsource.slang.api.LoopTree.LoopKind.WHILE; +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.assertj.core.api.Assertions.assertThat; + +class LoopTreeImplTest { + + @Test + void test() { + TreeMetaData meta = null; + Tree condition = new LiteralTreeImpl(meta, "1"); + Tree body = new IdentifierTreeImpl(meta, "x"); + + TokenImpl forToken = new TokenImpl(new TextRangeImpl(1, 0, 1, 3), "for", Token.Type.KEYWORD); + TokenImpl whileToken = new TokenImpl(new TextRangeImpl(1, 0, 1, 5), "while", Token.Type.KEYWORD); + TokenImpl doToken = new TokenImpl(new TextRangeImpl(1, 0, 1, 2), "do", Token.Type.KEYWORD); + + LoopTreeImpl forTree = new LoopTreeImpl(meta, condition, body, FOR, forToken); + assertThat(forTree.children()).containsExactly(condition, body); + assertThat(forTree.condition()).isEqualTo(condition); + assertThat(forTree.body()).isEqualTo(body); + assertThat(forTree.kind()).isEqualTo(FOR); + + LoopTreeImpl whileTree = new LoopTreeImpl(meta, condition, body, WHILE, whileToken); + LoopTreeImpl doTree = new LoopTreeImpl(meta, condition, body, DOWHILE, doToken); + + assertThat(areEquivalent(forTree, new LoopTreeImpl(meta, condition, body, FOR, forToken))).isTrue(); + assertThat(areEquivalent(forTree, whileTree)).isFalse(); + assertThat(areEquivalent(forTree, doTree)).isFalse(); + assertThat(areEquivalent(whileTree, doTree)).isFalse(); + + LoopTreeImpl forTreeWithoutCondition = new LoopTreeImpl(meta, null, body, FOR, forToken); + assertThat(forTreeWithoutCondition.condition()).isNull(); + assertThat(forTree.body()).isEqualTo(body); + assertThat(forTree.kind()).isEqualTo(FOR); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/MatchCaseTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/MatchCaseTreeImplTest.java new file mode 100644 index 00000000..1933bdcf --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/MatchCaseTreeImplTest.java @@ -0,0 +1,84 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Token.Type; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +import static java.util.Collections.emptyList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.impl.TextRanges.range; + +class MatchCaseTreeImplTest { + + private static final List TOKENS = Arrays.asList( + new TokenImpl(range(1, 1, 1, 5), "when", Type.OTHER), + new TokenImpl(range(1, 7, 1, 8), "1", Type.OTHER), + new TokenImpl(range(1, 9, 1, 10), "a", Type.OTHER)); + + @Test + void test() { + TreeMetaData meta = null; + Tree expression = new LiteralTreeImpl(meta, "42"); + Tree body = new IdentifierTreeImpl(meta, "x"); + MatchCaseTree tree = new MatchCaseTreeImpl(meta, expression, body); + assertThat(tree.children()).containsExactly(expression, body); + assertThat(tree.expression()).isEqualTo(expression); + assertThat(tree.body()).isEqualTo(body); + + assertThat(new MatchCaseTreeImpl(meta, null, body).children()).containsExactly(body); + assertThat(new MatchCaseTreeImpl(meta, expression, null).children()).containsExactly(expression); + } + + @Test + void rangeToHighlight() { + TreeMetaDataProvider metaDataProvider = new TreeMetaDataProvider(emptyList(), TOKENS); + TreeMetaData matchCaseMetaData = metaDataProvider.metaData(range(1, 1, 1, 10)); + TreeMetaData bodyMetaData = metaDataProvider.metaData(range(1, 9, 1, 10)); + BlockTree body = new BlockTreeImpl(bodyMetaData, Collections.singletonList(new IdentifierTreeImpl(bodyMetaData, "a"))); + + assertThat(new MatchCaseTreeImpl(matchCaseMetaData, null, body).rangeToHighlight()) + .isEqualTo(range(1, 1, 1, 8)); + } + + @Test + void rangeToHighlight_with_empty_body() { + TreeMetaDataProvider metaDataProvider = new TreeMetaDataProvider(emptyList(), TOKENS); + TreeMetaData matchCaseMetaData = metaDataProvider.metaData(range(1, 1, 1, 8)); + TreeMetaData bodyMetaData = metaDataProvider.metaData(range(1, 1, 1, 8)); + BlockTree body = new BlockTreeImpl(bodyMetaData, emptyList()); + + assertThat(new MatchCaseTreeImpl(matchCaseMetaData, null, body).rangeToHighlight()) + .isEqualTo(range(1, 1, 1, 8)); + + assertThat(new MatchCaseTreeImpl(matchCaseMetaData, null, null).rangeToHighlight()) + .isEqualTo(range(1, 1, 1, 8)); + } + + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/MatchTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/MatchTreeImplTest.java new file mode 100644 index 00000000..66b69bdb --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/MatchTreeImplTest.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import java.util.Collections; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class MatchTreeImplTest { + + @Test + void test() { + TreeMetaData meta = null; + Tree expression = new IdentifierTreeImpl(null, "x"); + MatchCaseTree case1 = new MatchCaseTreeImpl(null, null, new LiteralTreeImpl(meta, "42")); + Token keywordToken = new TokenImpl(new TextRangeImpl(1,0,1,20), "match", Token.Type.KEYWORD); + MatchTree tree = new MatchTreeImpl(meta, expression, Collections.singletonList(case1), keywordToken); + assertThat(tree.children()).containsExactly(expression, case1); + assertThat(tree.expression()).isEqualTo(expression); + assertThat(tree.cases()).containsExactly(case1); + assertThat(tree.keyword().text()).isEqualTo("match"); + } + + @Test + void without_expression() { + TreeMetaData meta = null; + MatchCaseTree case1 = new MatchCaseTreeImpl(null, null, new LiteralTreeImpl(meta, "42")); + Token keywordToken = new TokenImpl(new TextRangeImpl(1,0,1,20), "match", Token.Type.KEYWORD); + MatchTree tree = new MatchTreeImpl(meta, null, Collections.singletonList(case1), keywordToken); + assertThat(tree.children()).containsExactly(case1); + assertThat(tree.expression()).isNull(); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/MemberSelectTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/MemberSelectTreeImplTest.java new file mode 100644 index 00000000..0307cbd5 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/MemberSelectTreeImplTest.java @@ -0,0 +1,42 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.MemberSelectTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; + +import static org.assertj.core.api.Assertions.assertThat; + +class MemberSelectTreeImplTest { + + @Test + void test() { + TreeMetaData meta = null; + IdentifierTree identifierTree = new IdentifierTreeImpl(meta, "y"); + Tree member = new IdentifierTreeImpl(meta, "x"); + MemberSelectTree memberSelect = new MemberSelectTreeImpl(meta, member, identifierTree); + assertThat(memberSelect.children()).containsExactly(member, identifierTree); + assertThat(memberSelect.expression()).isEqualTo(member); + assertThat(memberSelect.identifier()).isEqualTo(identifierTree); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/PackageDeclarationTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/PackageDeclarationTreeImplTest.java new file mode 100644 index 00000000..ad5a745b --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/PackageDeclarationTreeImplTest.java @@ -0,0 +1,39 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Collections; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.PackageDeclarationTree; +import org.sonarsource.slang.api.Tree; + +import static org.sonarsource.slang.utils.TreeCreationUtils.identifier; + +class PackageDeclarationTreeImplTest { + + @Test + void test() { + Tree identifier = identifier("x"); + PackageDeclarationTree tree = new PackageDeclarationTreeImpl(null, Collections.singletonList(identifier)); + Assertions.assertThat(tree.children()).containsExactly(identifier); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/ParameterTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/ParameterTreeImplTest.java new file mode 100644 index 00000000..974ec7bb --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/ParameterTreeImplTest.java @@ -0,0 +1,127 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Collections; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.assertj.core.api.Assertions.assertThat; + +class ParameterTreeImplTest { + + private class TypeNativeKind implements NativeKind {} + + @Test + void test() { + TreeMetaData meta = null; + Tree parameterType = new NativeTreeImpl(meta, new TypeNativeKind(), null); + IdentifierTree identifierTreeX = new IdentifierTreeImpl(meta, "x"); + IdentifierTree identifierTreeY = new IdentifierTreeImpl(meta, "y"); + ParameterTreeImpl parameterTreeX = new ParameterTreeImpl(meta, identifierTreeX, null); + ParameterTreeImpl parameterTreeXCopy = new ParameterTreeImpl(meta, new IdentifierTreeImpl(meta, "x"), null); + ParameterTreeImpl parameterTreeXTyped = new ParameterTreeImpl(meta, identifierTreeX, parameterType); + ParameterTreeImpl parameterTreeY = new ParameterTreeImpl(meta, identifierTreeY, parameterType); + + assertThat(parameterTreeXTyped.children()).hasSize(2); + assertThat(parameterTreeX.children()).hasSize(1); + assertThat(parameterTreeX.type()).isNull(); + assertThat(parameterTreeX.identifier()).isEqualTo(identifierTreeX); + assertThat(areEquivalent(parameterTreeX, parameterTreeXCopy)).isTrue(); + assertThat(areEquivalent(parameterTreeX, parameterTreeXTyped)).isFalse(); + assertThat(areEquivalent(parameterTreeX, parameterTreeY)).isFalse(); + assertThat(areEquivalent(parameterTreeXTyped, parameterTreeY)).isFalse(); + } + + @Test + void test_default_value() { + TreeMetaData meta = null; + IdentifierTree identifierTreeX = new IdentifierTreeImpl(meta, "x"); + IdentifierTree identifierTreeY = new IdentifierTreeImpl(meta, "y"); + IdentifierTree defaultValue1 = new IdentifierTreeImpl(meta, "1"); + ParameterTreeImpl parameterTreeXDefault1 = new ParameterTreeImpl(meta, identifierTreeX, null, defaultValue1); + ParameterTreeImpl parameterTreeXDefault1Copy = new ParameterTreeImpl(meta, new IdentifierTreeImpl(meta, "x"), null, new IdentifierTreeImpl(meta, "1")); + ParameterTreeImpl parameterTreeXDefault2 = new ParameterTreeImpl(meta, identifierTreeX, null, new IdentifierTreeImpl(meta, "2")); + ParameterTreeImpl parameterTreeXDefaultNative = new ParameterTreeImpl(meta, identifierTreeX, null, new NativeTreeImpl(meta, new TypeNativeKind(), null)); + ParameterTreeImpl parameterTreeY = new ParameterTreeImpl(meta, identifierTreeY, null); + + + assertThat(parameterTreeXDefault1.children()).hasSize(2); + assertThat(parameterTreeXDefault2.children()).hasSize(2); + assertThat(parameterTreeXDefaultNative.children()).hasSize(2); + assertThat(parameterTreeY.children()).hasSize(1); + assertThat(parameterTreeXDefault1.defaultValue()).isEqualTo(defaultValue1); + assertThat(parameterTreeY.defaultValue()).isNull(); + + assertThat(areEquivalent(parameterTreeXDefault1, parameterTreeXDefault1Copy)).isTrue(); + assertThat(areEquivalent(parameterTreeXDefault1, parameterTreeXDefault2)).isFalse(); + assertThat(areEquivalent(parameterTreeXDefault1, parameterTreeXDefaultNative)).isFalse(); + assertThat(areEquivalent(parameterTreeXDefault1, parameterTreeY)).isFalse(); + } + + @Test + void test_modifiers() { + TreeMetaData meta = null; + IdentifierTree identifierTreeX = new IdentifierTreeImpl(meta, "x"); + IdentifierTree identifierTreeY = new IdentifierTreeImpl(meta, "y"); + Tree publicModifier = new ModifierTreeImpl(meta, ModifierTree.Kind.PUBLIC); + + ParameterTreeImpl parameterTreeXPublic = new ParameterTreeImpl(meta, identifierTreeX, null, null, + Collections.singletonList(publicModifier)); + ParameterTreeImpl parameterTreeXPublicCopy = new ParameterTreeImpl(meta, new IdentifierTreeImpl(meta, "x"), + null, null, Collections.singletonList(new ModifierTreeImpl(meta, ModifierTree.Kind.PUBLIC))); + ParameterTreeImpl parameterTreeXPrivate = new ParameterTreeImpl(meta, identifierTreeX, + null, null, Collections.singletonList(new ModifierTreeImpl(meta, ModifierTree.Kind.PRIVATE))); + ParameterTreeImpl parameterTreeXNative = new ParameterTreeImpl(meta, identifierTreeX, null, null, + Collections.singletonList(new NativeTreeImpl(meta, new TypeNativeKind(), null))); + ParameterTreeImpl parameterTreeNoMod = new ParameterTreeImpl(meta, identifierTreeY, null); + + + assertThat(parameterTreeXPublic.children()).hasSize(2); + assertThat(parameterTreeXPrivate.children()).hasSize(2); + assertThat(parameterTreeXNative.children()).hasSize(2); + assertThat(parameterTreeNoMod.children()).hasSize(1); + assertThat(parameterTreeXPublic.modifiers()).hasSize(1); + assertThat(parameterTreeXPublic.modifiers().get(0)).isEqualTo(publicModifier); + assertThat(parameterTreeNoMod.modifiers()).isEmpty(); + + assertThat(areEquivalent(parameterTreeXPublic, parameterTreeXPublicCopy)).isTrue(); + assertThat(areEquivalent(parameterTreeXPublic, parameterTreeXPrivate)).isFalse(); + assertThat(areEquivalent(parameterTreeXPublic, parameterTreeXNative)).isFalse(); + assertThat(areEquivalent(parameterTreeXPublic, parameterTreeNoMod)).isFalse(); + } + + @Test + void test_null_identifier() { + TreeMetaData meta = null; + Tree publicModifier = new ModifierTreeImpl(meta, ModifierTree.Kind.PUBLIC); + ParameterTreeImpl parameterTreePublic = new ParameterTreeImpl(meta, null, null, null, + Collections.singletonList(publicModifier)); + + assertThat(parameterTreePublic.children()).hasSize(1); + assertThat(parameterTreePublic.identifier()).isNull(); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/ParenthesizedExpressionTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/ParenthesizedExpressionTreeImplTest.java new file mode 100644 index 00000000..aac20328 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/ParenthesizedExpressionTreeImplTest.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.ParenthesizedExpressionTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.impl.TextRanges.range; +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.sonarsource.slang.utils.TreeCreationUtils.binary; +import static org.sonarsource.slang.utils.TreeCreationUtils.identifier; +import static org.sonarsource.slang.utils.TreeCreationUtils.integerLiteral; +import static org.sonarsource.slang.utils.TreeCreationUtils.literal; +import static org.assertj.core.api.Assertions.assertThat; + +class ParenthesizedExpressionTreeImplTest { + + @Test + void test() { + Tree identifier = identifier("value"); + Tree literalInt2 = integerLiteral("2"); + Tree literalTrue = literal("true"); + Tree binary1 = binary(BinaryExpressionTree.Operator.GREATER_THAN, identifier, literalInt2); + Token leftParenthesis1 = new TokenImpl(range(5, 1, 5, 2), "(", Token.Type.OTHER); + Token rightParenthesis1 = new TokenImpl(range(5, 6, 5, 7), ")", Token.Type.OTHER); + Token leftParenthesis2 = new TokenImpl(range(5, 0, 5, 1), "(", Token.Type.OTHER); + Token rightParenthesis2 = new TokenImpl(range(5, 10, 5, 11), ")", Token.Type.OTHER); + + ParenthesizedExpressionTree parenthesisExpression1 = new ParenthesizedExpressionTreeImpl(null, binary1, leftParenthesis1, rightParenthesis1); + Tree binary2 = binary(BinaryExpressionTree.Operator.EQUAL_TO, literalTrue, parenthesisExpression1); + ParenthesizedExpressionTree parenthesisExpression2 = new ParenthesizedExpressionTreeImpl(null, binary2, leftParenthesis2, rightParenthesis2); + + assertThat(parenthesisExpression1.children()).hasSize(1); + assertThat(parenthesisExpression2.children()).hasSize(1); + assertThat(areEquivalent(parenthesisExpression1, parenthesisExpression1)).isTrue(); + assertThat(areEquivalent(parenthesisExpression1, new ParenthesizedExpressionTreeImpl(null, binary1, null, null))).isTrue(); + assertThat(areEquivalent(parenthesisExpression1, parenthesisExpression2)).isFalse(); + assertThat(parenthesisExpression1.expression()).isEqualTo(binary1); + assertThat(parenthesisExpression2.expression()).isEqualTo(binary2); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/PlaceHolderTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/PlaceHolderTreeImplTest.java new file mode 100644 index 00000000..04890a19 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/PlaceHolderTreeImplTest.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.PlaceHolderTree; +import org.sonarsource.slang.api.Token; + +import static org.assertj.core.api.Assertions.assertThat; + +class PlaceHolderTreeImplTest { + + @Test + void test_place_holder() { + TokenImpl keyword = new TokenImpl(new TextRangeImpl(1, 0, 1, 1), "_", Token.Type.OTHER); + PlaceHolderTree placeHolderTree = new PlaceHolderTreeImpl(null, keyword); + assertThat(placeHolderTree.children()).isEmpty(); + assertThat(placeHolderTree.placeHolderToken().text()).isEqualTo("_"); + assertThat(placeHolderTree.placeHolderToken().type()).isEqualTo(Token.Type.OTHER); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/ReturnTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/ReturnTreeImplTest.java new file mode 100644 index 00000000..f53e5dab --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/ReturnTreeImplTest.java @@ -0,0 +1,50 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TreeMetaData; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.assertj.core.api.Assertions.assertThat; + +class ReturnTreeImplTest { + @Test + void test() { + TreeMetaData meta = null; + TokenImpl returnKeyword = new TokenImpl(new TextRangeImpl(1, 0, 1, 6), "return", Token.Type.KEYWORD); + ReturnTreeImpl returnWithoutValue = new ReturnTreeImpl(meta, returnKeyword, null); + + assertThat(returnWithoutValue.children()).isEmpty(); + assertThat(returnWithoutValue.keyword().text()).isEqualTo("return"); + assertThat(returnWithoutValue.body()).isNull(); + + ReturnTreeImpl returnWithValue = new ReturnTreeImpl(meta, returnKeyword, new LiteralTreeImpl(meta, "foo")); + assertThat(returnWithValue.children()).hasSize(1); + assertThat(returnWithValue.keyword().text()).isEqualTo("return"); + assertThat(returnWithValue.body()).isInstanceOf(LiteralTree.class); + + assertThat(areEquivalent(returnWithoutValue, new ReturnTreeImpl(meta, returnKeyword, null))).isTrue(); + assertThat(areEquivalent(returnWithoutValue, returnWithValue)).isFalse(); + + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/TextPointerImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/TextPointerImplTest.java new file mode 100644 index 00000000..695fdd12 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/TextPointerImplTest.java @@ -0,0 +1,53 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TextPointerImplTest { + + @Test + void test_equals() { + TextPointerImpl p1 = new TextPointerImpl(1, 2); + assertThat(p1) + .isEqualTo(p1) + .isEqualTo(new TextPointerImpl(1, 2)) + .isNotEqualTo(new TextPointerImpl(1, 3)) + .isNotEqualTo(new TextPointerImpl(3, 2)) + .isNotEqualTo(null) + .isNotEqualTo(""); + } + + @Test + void test_hashCode() { + assertThat(new TextPointerImpl(1, 2).hashCode()).isEqualTo(new TextPointerImpl(1, 2).hashCode()); + assertThat(new TextPointerImpl(1, 2).hashCode()).isNotEqualTo(new TextPointerImpl(1, 3).hashCode()); + } + + @Test + void test_compareTo() { + assertThat(new TextPointerImpl(1, 2)).isEqualByComparingTo(new TextPointerImpl(1, 2)); + assertThat(new TextPointerImpl(1, 2).compareTo(new TextPointerImpl(1, 4))).isEqualTo(-1); + assertThat(new TextPointerImpl(1, 2).compareTo(new TextPointerImpl(2, 1))).isEqualTo(-1); + assertThat(new TextPointerImpl(1, 2).compareTo(new TextPointerImpl(1, 1))).isEqualTo(1); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/TextRangeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/TextRangeImplTest.java new file mode 100644 index 00000000..4e3ff99d --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/TextRangeImplTest.java @@ -0,0 +1,54 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.TextPointer; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TextRangeImplTest { + + private TextPointer p1 = new TextPointerImpl(1, 2); + private TextPointer p2 = new TextPointerImpl(3, 4); + + @Test + void test_equals() { + TextRangeImpl range1 = new TextRangeImpl(p1, p2); + assertThat(range1) + .isEqualTo(range1) + .isEqualTo(new TextRangeImpl(p1, p2)) + .isNotEqualTo(new TextRangeImpl(p1, p1)) + .isNotEqualTo(new TextRangeImpl(p2, p2)) + .isNotEqualTo(null) + .isNotEqualTo(""); + } + + @Test + void test_hashCode() { + assertThat(new TextRangeImpl(p1, p2)).hasSameHashCodeAs(new TextRangeImpl(p1, p2)); + assertThat(new TextRangeImpl(p1, p2).hashCode()).isNotEqualTo(new TextRangeImpl(p1, p1).hashCode()); + } + + @Test + void test_toString() { + assertThat(new TextRangeImpl(p1, p2)).hasToString("TextRange[1, 2, 3, 4]"); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/TextRangesTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/TextRangesTest.java new file mode 100644 index 00000000..c93250c2 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/TextRangesTest.java @@ -0,0 +1,49 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.TextRange; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.sonarsource.slang.impl.TextRanges.range; +import static org.assertj.core.api.Assertions.assertThat; + +class TextRangesTest { + + @Test + void merge_not_empty_list() { + assertThat(merge(range(1, 2, 3, 4))).isEqualTo(range(1, 2, 3, 4)); + assertThat(merge(range(1, 2, 3, 4), range(5, 1, 5, 7))).isEqualTo(range(1, 2, 5, 7)); + assertThat(merge(range(1, 2, 3, 4), range(1, 3, 1, 5))).isEqualTo(range(1, 2, 3, 4)); + } + + @Test + void merge_empty_list() { + assertThrows(IllegalArgumentException.class, + TextRangesTest::merge); + } + + private static TextRange merge(TextRange... ranges) { + return TextRanges.merge(Arrays.asList(ranges)); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/ThrowTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/ThrowTreeImplTest.java new file mode 100644 index 00000000..425208a5 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/ThrowTreeImplTest.java @@ -0,0 +1,50 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TreeMetaData; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; + +class ThrowTreeImplTest { + @Test + void test() { + TreeMetaData meta = null; + TokenImpl throwKeyword = new TokenImpl(new TextRangeImpl(1, 0, 1, 6), "throw", Token.Type.KEYWORD); + ThrowTreeImpl throwWithoutValue = new ThrowTreeImpl(meta, throwKeyword, null); + + assertThat(throwWithoutValue.children()).isEmpty(); + assertThat(throwWithoutValue.keyword().text()).isEqualTo("throw"); + assertThat(throwWithoutValue.body()).isNull(); + + ThrowTreeImpl throwWithValue = new ThrowTreeImpl(meta, throwKeyword, new LiteralTreeImpl(meta, "foo")); + assertThat(throwWithValue.children()).hasSize(1); + assertThat(throwWithValue.keyword().text()).isEqualTo("throw"); + assertThat(throwWithValue.body()).isInstanceOf(LiteralTree.class); + + assertThat(areEquivalent(throwWithoutValue, new ThrowTreeImpl(meta, throwKeyword, null))).isTrue(); + assertThat(areEquivalent(throwWithoutValue, throwWithValue)).isFalse(); + + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/TreeMetaDataProviderTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/TreeMetaDataProviderTest.java new file mode 100644 index 00000000..454f1d0e --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/TreeMetaDataProviderTest.java @@ -0,0 +1,253 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import java.util.Collections; +import java.util.List; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import java.util.Arrays; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.TreeMetaData; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.sonarsource.slang.impl.TextRanges.range; +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; + +class TreeMetaDataProviderTest { + + @Test + void commentsInside() { + Comment comment = new CommentImpl("// comment1", "comment1", range(2, 5, 2, 12), range(2, 7, 2, 12)); + TreeMetaDataProvider provider = new TreeMetaDataProvider(singletonList(comment), emptyList()); + assertThat(provider.allComments()).hasSize(1); + assertThat(provider.metaData(new TextRangeImpl(1, 1, 1, 20)).commentsInside()).isEmpty(); + assertThat(provider.metaData(new TextRangeImpl(2, 1, 2, 20)).commentsInside()).containsExactly(comment); + assertThat(provider.metaData(new TextRangeImpl(2, 5, 2, 20)).commentsInside()).containsExactly(comment); + } + + @Test + void single_annotation() { + Annotation annotation = new AnnotationImpl("MyAnnotation", emptyList(), range(2, 5, 2, 13)); + Token token1 = new TokenImpl(new TextRangeImpl(2, 5, 2, 6), "@", Token.Type.OTHER); + Token token2 = new TokenImpl(new TextRangeImpl(2, 6, 2, 13), "MyAnnotation", Token.Type.OTHER); + + Token token3 = new TokenImpl(new TextRangeImpl(3, 5, 3, 6), "class", Token.Type.OTHER); + + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2, token3), singletonList(annotation)); + + List oneAnnotation = provider.metaData(new TextRangeImpl(2, 5, 25, 25)).annotations(); + assertThat(oneAnnotation).containsExactly(annotation); + } + + @Test + void multiple_annotations() { + Annotation annotation1 = new AnnotationImpl("MyAnnotation", emptyList(), range(2, 5, 2, 13)); + Token token1 = new TokenImpl(new TextRangeImpl(2, 5, 2, 6), "@", Token.Type.OTHER); + Token token2 = new TokenImpl(new TextRangeImpl(2, 6, 2, 13), "MyAnnotation", Token.Type.OTHER); + + Annotation annotation2 = new AnnotationImpl("MyAnnotation2", Collections.singletonList("abc"), range(3, 5, 3, 14)); + Token token3 = new TokenImpl(new TextRangeImpl(3, 5, 3, 6), "@", Token.Type.OTHER); + Token token4 = new TokenImpl(new TextRangeImpl(3, 6, 3, 14), "MyAnnotation2", Token.Type.OTHER); + + // A token between the second and the third annotation + Token token5 = new TokenImpl(new TextRangeImpl(4, 6, 4, 9), "fun", Token.Type.OTHER); + + Annotation annotation3 = new AnnotationImpl("MyAnnotation3", emptyList(), range(5, 5, 5, 14)); + Token token6 = new TokenImpl(new TextRangeImpl(5, 5, 5, 6), "@", Token.Type.OTHER); + Token token7 = new TokenImpl(new TextRangeImpl(5, 6, 5, 14), "MyAnnotation3", Token.Type.OTHER); + + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), + Arrays.asList(token1, token2, token3, token4, token5, token6, token7), + Arrays.asList(annotation1, annotation2, annotation3)); + // Annotations have to start the same place as the range. All annotations directly following the first one will be returned. + + List twoAnnotations = provider.metaData(new TextRangeImpl(2, 5, 10, 13)).annotations(); + assertThat(twoAnnotations).containsExactly(annotation1, annotation2); + Annotation firstAnnotation = twoAnnotations.get(0); + assertThat(firstAnnotation.shortName()).isEqualTo("MyAnnotation"); + assertThat(firstAnnotation.argumentsText()).isEmpty(); + Annotation secondAnnotation = twoAnnotations.get(1); + assertThat(secondAnnotation.shortName()).isEqualTo("MyAnnotation2"); + assertThat(secondAnnotation.argumentsText()).containsExactly("abc"); + // The end position does not matters + assertThat(provider.metaData(new TextRangeImpl(2, 5, 25, 14)).annotations()).containsExactly(annotation1, annotation2); + assertThat(provider.metaData(new TextRangeImpl(2, 6, 10, 13)).annotations()).isEmpty(); + assertThat(provider.metaData(new TextRangeImpl(3, 5, 10, 13)).annotations()).containsExactly(annotation2); + assertThat(provider.metaData(new TextRangeImpl(3, 1, 10, 13)).annotations()).isEmpty(); + assertThat(provider.metaData(new TextRangeImpl(9, 1, 10, 13)).annotations()).isEmpty(); + assertThat(provider.metaData(new TextRangeImpl(5, 5, 10, 6)).annotations()).containsExactly(annotation3); + } + + @Test + void annotations_are_cached() { + Annotation annotation = new AnnotationImpl("MyAnnotation", emptyList(), range(2, 5, 2, 13)); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), emptyList(), singletonList(annotation)); + TreeMetaData metaData = provider.metaData(new TextRangeImpl(2, 5, 2, 13)); + List firstCall = metaData.annotations(); + List secondCall = metaData.annotations(); + assertThat(firstCall).isSameAs(secondCall); + } + + @Test + void tokens() { + Token token1 = new TokenImpl(new TextRangeImpl(1, 3, 1, 6), "abc", Token.Type.OTHER); + Token token2 = new TokenImpl(new TextRangeImpl(1, 9, 1, 12), "abc", Token.Type.OTHER); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2)); + assertThat(provider.allTokens()).hasSize(2); + assertThat(provider.metaData(new TextRangeImpl(1, 1, 1, 20)).tokens()).containsExactly(token1, token2); + assertThat(provider.metaData(new TextRangeImpl(1, 3, 1, 8)).tokens()).containsExactly(token1); + assertThat(provider.metaData(new TextRangeImpl(1, 3, 1, 6)).tokens()).containsExactly(token1); + } + + @Test + void lines_of_code() { + Token token1 = new TokenImpl(new TextRangeImpl(1, 3, 1, 6), "abc", Token.Type.OTHER); + Token token2 = new TokenImpl(new TextRangeImpl(1, 9, 1, 12), "def", Token.Type.OTHER); + Token token3 = new TokenImpl(new TextRangeImpl(2, 1, 2, 4), "abc", Token.Type.OTHER); + Token token4 = new TokenImpl(new TextRangeImpl(4, 1, 6, 2), "ab\ncd\nef", Token.Type.OTHER); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2, token3, token4)); + TreeMetaData metaData = provider.metaData(new TextRangeImpl(1, 1, 1, 20)); + assertThat(metaData.linesOfCode()).containsExactly(1); + assertThat(metaData.linesOfCode()).containsExactly(1); + assertThat(metaData.textRange()).hasToString("TextRange[1, 1, 1, 20]"); + assertThat(provider.metaData(new TextRangeImpl(1, 1, 2, 20)).linesOfCode()).containsExactly(1, 2); + assertThat(provider.metaData(new TextRangeImpl(1, 1, 3, 20)).linesOfCode()).containsExactly(1, 2); + assertThat(provider.metaData(new TextRangeImpl(1, 1, 6, 20)).linesOfCode()).containsExactly(1, 2, 4, 5, 6); + } + + @Test + void keyword() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.KEYWORD); + Token token2 = new TokenImpl(range(1, 4, 1, 6), "cd", Token.Type.KEYWORD); + Token token3 = new TokenImpl(range(1, 6, 1, 7), "{", Token.Type.OTHER); + Token token4 = new TokenImpl(range(1, 7, 1, 8), "ef", Token.Type.OTHER); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2, token3, token4)); + assertThat(provider.keyword(range(1, 3, 1, 7))).isEqualTo(token2); + assertThat(provider.keyword(range(1, 3, 1, 8))).isEqualTo(token2); + TextRange range3to4 = range(1, 3, 1, 4); + assertThatThrownBy(() -> provider.keyword(range3to4)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Cannot find single keyword in TextRange[1, 3, 1, 4]"); + TextRange range1to7 = range(1, 1, 1, 7); + assertThatThrownBy(() -> provider.keyword(range1to7)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Cannot find single keyword in TextRange[1, 1, 1, 7]"); + } + + @Test + void all_tokens() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.KEYWORD); + Token token2 = new TokenImpl(range(1, 4, 1, 6), "cd", Token.Type.KEYWORD); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2)); + List allTokens = provider.allTokens(); + assertThat(allTokens).hasSize(2); + assertThat(allTokens.get(0).text()).isEqualTo("ab"); + assertThat(allTokens.get(1).text()).isEqualTo("cd"); + } + + @Test + void index_of_first_token() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.KEYWORD); + Token token2 = new TokenImpl(range(1, 4, 1, 6), "cd", Token.Type.KEYWORD); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2)); + assertThat(provider.indexOfFirstToken(range(1, 0, 1, 1))).isEqualTo(-1); + assertThat(provider.indexOfFirstToken(range(1, 0, 1, 2))).isEqualTo(-1); + assertThat(provider.indexOfFirstToken(range(1, 0, 1, 3))).isZero(); + assertThat(provider.indexOfFirstToken(range(1, 1, 1, 3))).isZero(); + assertThat(provider.indexOfFirstToken(range(1, 2, 1, 3))).isEqualTo(-1); + assertThat(provider.indexOfFirstToken(range(1, 2, 1, 6))).isEqualTo(1); + assertThat(provider.indexOfFirstToken(range(1, 4, 1, 6))).isEqualTo(1); + assertThat(provider.indexOfFirstToken(range(1, 4, 2, 0))).isEqualTo(1); + assertThat(provider.indexOfFirstToken(range(1, 4, 1, 5))).isEqualTo(-1); + assertThat(provider.indexOfFirstToken(range(1, 5, 1, 10))).isEqualTo(-1); + assertThat(provider.indexOfFirstToken(range(1, 20, 1, 22))).isEqualTo(-1); + } + + @Test + void first_token() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.KEYWORD); + Token token2 = new TokenImpl(range(1, 4, 1, 6), "cd", Token.Type.KEYWORD); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2)); + assertThat(provider.firstToken(range(1, 0, 1, 1))).isNotPresent(); + assertThat(provider.firstToken(range(1, 1, 1, 3)).get().text()).isEqualTo("ab"); + assertThat(provider.firstToken(range(1, 2, 1, 20)).get().text()).isEqualTo("cd"); + assertThat(provider.firstToken(range(1, 5, 1, 20))).isNotPresent(); + } + + @Test + void previous_token() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.KEYWORD); + Token token2 = new TokenImpl(range(1, 4, 1, 6), "cd", Token.Type.KEYWORD); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2)); + + assertThat(provider.previousToken(range(1, 0, 1, 1))).isNotPresent(); + assertThat(provider.previousToken(range(1, 1, 1, 3))).isNotPresent(); + assertThat(provider.previousToken(range(1, 2, 1, 20)).get().text()).isEqualTo("ab"); + assertThat(provider.previousToken(range(1, 5, 1, 20))).isNotPresent(); + } + + @Test + void previous_token_with_expected_value() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.KEYWORD); + Token token2 = new TokenImpl(range(1, 4, 1, 6), "cd", Token.Type.KEYWORD); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2)); + + assertThat(provider.previousToken(range(1, 2, 1, 20), "ef")).isNotPresent(); + assertThat(provider.previousToken(range(1, 2, 1, 20), "ab")).isPresent(); + assertThat(provider.previousToken(range(1, 2, 1, 20), "AB")).isNotPresent(); + + assertThat(provider.previousToken(range(1, 2, 1, 20), token -> true)).isPresent(); + assertThat(provider.previousToken(range(1, 2, 1, 20), token -> false)).isNotPresent(); + } + + @Test + void update_token_type() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.OTHER); + Token token2 = new TokenImpl(range(1, 4, 1, 6), "cd", Token.Type.OTHER); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1, token2)); + List allTokens = provider.allTokens(); + assertThat(allTokens).hasSize(2); + provider.updateTokenType(allTokens.get(0), Token.Type.KEYWORD); + assertThat(allTokens.get(0).text()).isEqualTo("ab"); + assertThat(allTokens.get(0).type()).isEqualTo(Token.Type.KEYWORD); + assertThat(allTokens.get(1).text()).isEqualTo("cd"); + assertThat(allTokens.get(1).type()).isEqualTo(Token.Type.OTHER); + } + + @Test + void error_when_updating_token_type() { + Token token1 = new TokenImpl(range(1, 1, 1, 3), "ab", Token.Type.OTHER); + TreeMetaDataProvider provider = new TreeMetaDataProvider(emptyList(), Arrays.asList(token1)); + + Token tokenNotInMetaData1 = new TokenImpl(range(1, 0, 1, 3), "xyz", Token.Type.OTHER); + assertThatThrownBy(() -> provider.updateTokenType(tokenNotInMetaData1, Token.Type.KEYWORD)) + .hasMessage("token 'xyz' not found in metadata, TextRange[1, 0, 1, 3]"); + + Token tokenNotInMetaData2 = new TokenImpl(range(1, 20, 1, 23), "xyz", Token.Type.OTHER); + assertThatThrownBy(() -> provider.updateTokenType(tokenNotInMetaData2, Token.Type.KEYWORD)) + .hasMessage("token 'xyz' not found in metadata, TextRange[1, 20, 1, 23]"); + + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/UnaryExpressionTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/UnaryExpressionTreeImplTest.java new file mode 100644 index 00000000..1a032b7d --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/UnaryExpressionTreeImplTest.java @@ -0,0 +1,51 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.assertj.core.api.Assertions.assertThat; + +class UnaryExpressionTreeImplTest { + + private class TypeNativeKind implements NativeKind {} + + @Test + void test() { + TreeMetaData meta = null; + Tree condition = new IdentifierTreeImpl(meta, "x"); + Tree negCondition = new UnaryExpressionTreeImpl(meta, UnaryExpressionTree.Operator.NEGATE, condition); + Tree negConditionCopy = new UnaryExpressionTreeImpl(meta, UnaryExpressionTree.Operator.NEGATE, condition); + Tree nativeTree = new NativeTreeImpl(meta, new TypeNativeKind(), Arrays.asList(condition)); + Tree negNative = new UnaryExpressionTreeImpl(meta, UnaryExpressionTree.Operator.NEGATE, nativeTree); + + assertThat(negCondition.children()).containsExactly(condition); + assertThat(areEquivalent(negCondition, negConditionCopy)).isTrue(); + assertThat(areEquivalent(negNative, negCondition)).isFalse(); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/VariableDeclarationTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/VariableDeclarationTreeImplTest.java new file mode 100644 index 00000000..d07c4816 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/VariableDeclarationTreeImplTest.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl; + +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.assertj.core.api.Assertions.assertThat; + +class VariableDeclarationTreeImplTest { + + private class TypeNativeKind implements NativeKind {} + + @Test + void test() { + TreeMetaData meta = null; + Tree variableType = new NativeTreeImpl(meta, new TypeNativeKind(), null); + IdentifierTree identifierTreeX = new IdentifierTreeImpl(meta, "x"); + IdentifierTree identifierTreeY = new IdentifierTreeImpl(meta, "y"); + VariableDeclarationTreeImpl variableTreeX = new VariableDeclarationTreeImpl(meta, identifierTreeX, null,null, false); + VariableDeclarationTreeImpl variableTreeXCopy = new VariableDeclarationTreeImpl(meta, new IdentifierTreeImpl(meta, "x"), null, null,false); + VariableDeclarationTreeImpl valueTreeX = new VariableDeclarationTreeImpl(meta, new IdentifierTreeImpl(meta, "x"), null, null, true); + VariableDeclarationTreeImpl variableTreeXTyped = new VariableDeclarationTreeImpl(meta, identifierTreeX, variableType, null, false); + VariableDeclarationTreeImpl variableTreeY = new VariableDeclarationTreeImpl(meta, identifierTreeY, variableType, null, false); + + assertThat(variableTreeXTyped.children()).hasSize(2); + assertThat(variableTreeX.children()).hasSize(1); + assertThat(variableTreeX.type()).isNull(); + assertThat(variableTreeX.identifier()).isEqualTo(identifierTreeX); + assertThat(areEquivalent(variableTreeX, variableTreeXCopy)).isTrue(); + assertThat(areEquivalent(variableTreeX, valueTreeX)).isFalse(); + assertThat(areEquivalent(variableTreeX, variableTreeXTyped)).isFalse(); + assertThat(areEquivalent(variableTreeX, variableTreeY)).isFalse(); + assertThat(areEquivalent(variableTreeXTyped, variableTreeY)).isFalse(); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/impl/literal/StringLiteralTreeImplTest.java b/slang-api/src/test/java/org/sonarsource/slang/impl/literal/StringLiteralTreeImplTest.java new file mode 100644 index 00000000..887dc6f5 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/impl/literal/StringLiteralTreeImplTest.java @@ -0,0 +1,52 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.impl.literal; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.impl.StringLiteralTreeImpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class StringLiteralTreeImplTest { + + @Test + void test() { + StringLiteralTreeImpl stringLiteral = new StringLiteralTreeImpl(null, "\"abc\""); + assertThat(stringLiteral.value()).isEqualTo("\"abc\""); + assertThat(stringLiteral.content()).isEqualTo("abc"); + assertThat(stringLiteral.children()).isEmpty(); + } + + @Test + void test_failure() { + assertThrows(IllegalArgumentException.class, + () -> new StringLiteralTreeImpl(null, "abc")); + } + + @Test + void test_explicit_content() { + StringLiteralTreeImpl stringLiteral = new StringLiteralTreeImpl(null, "abc", "abc"); + assertThat(stringLiteral.value()).isEqualTo("abc"); + assertThat(stringLiteral.content()).isEqualTo("abc"); + assertThat(stringLiteral.children()).isEmpty(); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/persistence/JsonTestHelper.java b/slang-api/src/test/java/org/sonarsource/slang/persistence/JsonTestHelper.java new file mode 100644 index 00000000..379e12b7 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/persistence/JsonTestHelper.java @@ -0,0 +1,158 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.WriterConfig; +import java.io.IOException; +import java.lang.reflect.Method; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.CommentImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.TextRanges; +import org.sonarsource.slang.impl.TokenImpl; +import org.sonarsource.slang.impl.TreeMetaDataProvider; + +import static org.assertj.core.api.Assertions.assertThat; + +public class JsonTestHelper { + + protected TreeMetaDataProvider metaDataProvider = new TreeMetaDataProvider(Collections.emptyList(), Collections.emptyList()); + + protected Token token(int line, int lineOffset, String text, Token.Type type) { + TextRange tokenRange = new TextRangeImpl(line, lineOffset, line, lineOffset + text.length()); + Token token = new TokenImpl(tokenRange, text, type); + metaDataProvider.allTokens().add(token); + metaDataProvider.allTokens().sort(TreeMetaDataProvider.COMPARATOR); + return token; + } + + protected Token stringToken(int line, int lineOffset, String text) { + return token(line, lineOffset, text, Token.Type.STRING_LITERAL); + } + + protected Comment comment(int line, int lineOffset, String commentText, int prefixLength, int suffixLength) { + String commentContentText = commentText.substring(prefixLength, commentText.length() - suffixLength); + TextRange commentRange = new TextRangeImpl(line, lineOffset, line, lineOffset + commentText.length()); + TextRange commentContentRange = new TextRangeImpl(line, lineOffset + prefixLength, + line, lineOffset + commentText.length() - suffixLength); + CommentImpl comment = new CommentImpl(commentText, commentContentText, commentRange, commentContentRange); + metaDataProvider.allComments().add(comment); + metaDataProvider.allComments().sort(TreeMetaDataProvider.COMPARATOR); + return comment; + } + + protected Token otherToken(int line, int lineOffset, String text) { + return token(line, lineOffset, text, Token.Type.OTHER); + } + + protected Token keywordToken(int line, int lineOffset, String text) { + return token(line, lineOffset, text, Token.Type.KEYWORD); + } + + protected TreeMetaData metaData(TextRange textRange) { + return metaDataProvider.metaData(textRange); + } + + protected TreeMetaData metaData(Token token) { + return metaData(token.textRange()); + } + + protected TreeMetaData metaData(HasTextRange from, HasTextRange to) { + return metaData(TextRanges.merge(Arrays.asList(from.textRange(), to.textRange()))); + } + + protected static String indentedJson(String json) throws IOException { + return Json.parse(json).toString(WriterConfig.PRETTY_PRINT); + } + + protected static String indentedJsonFromFile(String fileName) throws IOException { + Path path = Paths.get("src", "test", "resources", "org", "sonarsource", "slang", "persistence", fileName); + return indentedJson(new String(Files.readAllBytes(path), StandardCharsets.UTF_8)); + } + + public static T checkJsonSerializationDeserialization(T initialTree, String fileName) throws IOException { + String initialTreeAsJson = indentedJson(JsonTree.toJson(initialTree)); + String expectedJson = indentedJsonFromFile(fileName); + assertThat(initialTreeAsJson) + .describedAs("Comparing tree serialized into json with " + fileName) + .isEqualTo(expectedJson); + + T loadedTree = (T) JsonTree.fromJson(initialTreeAsJson); + String loadedTreeAsJson = indentedJson(JsonTree.toJson(loadedTree)); + assertThat(loadedTreeAsJson) + .describedAs("Comparing tree de-serialized/serialized into json with " + fileName) + .isEqualTo(expectedJson); + + return loadedTree; + } + + public static String tokens(Tree tree) { + return tokens(tree.metaData()); + } + + public static String tokens(TreeMetaData metaData) { + return tokens(metaData.tokens()); + } + + public static String tokens(List tokens) { + if (tokens.isEmpty()) { + return ""; + } + TextPointer start = tokens.get(0).textRange().start(); + TextPointer end = tokens.get(tokens.size() - 1).textRange().end(); + return start.line() + ":" + start.lineOffset() + ":" + end.line() + ":" + end.lineOffset() + " - " + + tokens.stream().map(Token::text).collect(Collectors.joining(" ")); + } + + public static String token(Token token) { + TextPointer start = token.textRange().start(); + TextPointer end = token.textRange().end(); + return start.line() + ":" + start.lineOffset() + ":" + end.line() + ":" + end.lineOffset() + " - " + + token.text(); + } + + public static List methodNames(Class cls) { + List ignoredMethods = Arrays.asList( + "children", "descendants", "metaData", "textRange", "wait", "equals", "toString", + "hashCode", "getClass", "notify", "notifyAll"); + return new ArrayList<>(Stream.of(cls.getMethods()) + .map(Method::getName) + .filter(name -> !ignoredMethods.contains(name)) + .collect(Collectors.toSet())); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/persistence/JsonTreeTest.java b/slang-api/src/test/java/org/sonarsource/slang/persistence/JsonTreeTest.java new file mode 100644 index 00000000..bfe19f0f --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/persistence/JsonTreeTest.java @@ -0,0 +1,751 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.CatchTree; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.ExceptionHandlingTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.ImportDeclarationTree; +import org.sonarsource.slang.api.IntegerLiteralTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.PackageDeclarationTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.ParenthesizedExpressionTree; +import org.sonarsource.slang.api.PlaceHolderTree; +import org.sonarsource.slang.api.ReturnTree; +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.api.ThrowTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.impl.AssignmentExpressionTreeImpl; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.BlockTreeImpl; +import org.sonarsource.slang.impl.CatchTreeImpl; +import org.sonarsource.slang.impl.ClassDeclarationTreeImpl; +import org.sonarsource.slang.impl.ExceptionHandlingTreeImpl; +import org.sonarsource.slang.impl.FunctionDeclarationTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.IfTreeImpl; +import org.sonarsource.slang.impl.ImportDeclarationTreeImpl; +import org.sonarsource.slang.impl.IntegerLiteralTreeImpl; +import org.sonarsource.slang.impl.JumpTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.LoopTreeImpl; +import org.sonarsource.slang.impl.MatchCaseTreeImpl; +import org.sonarsource.slang.impl.MatchTreeImpl; +import org.sonarsource.slang.impl.ModifierTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.PackageDeclarationTreeImpl; +import org.sonarsource.slang.impl.ParameterTreeImpl; +import org.sonarsource.slang.impl.ParenthesizedExpressionTreeImpl; +import org.sonarsource.slang.impl.PlaceHolderTreeImpl; +import org.sonarsource.slang.impl.ReturnTreeImpl; +import org.sonarsource.slang.impl.StringLiteralTreeImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.ThrowTreeImpl; +import org.sonarsource.slang.impl.TopLevelTreeImpl; +import org.sonarsource.slang.impl.UnaryExpressionTreeImpl; +import org.sonarsource.slang.impl.VariableDeclarationTreeImpl; +import org.sonarsource.slang.persistence.conversion.StringNativeKind; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.BODY; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CASES; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CATCH_BLOCK; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CATCH_BLOCKS; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CATCH_PARAMETER; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CLASS_TREE; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CONDITION; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CONTENT; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.DECLARATIONS; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.DEFAULT_VALUE; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.ELSE_BRANCH; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.ELSE_KEYWORD; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.EXPRESSION; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.FINALLY_BLOCK; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.FIRST_CPD_TOKEN; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.FORMAL_PARAMETERS; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.IDENTIFIER; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.IF_KEYWORD; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.INITIALIZER; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.IS_CONSTRUCTOR; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.IS_VAL; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.KEYWORD; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.KIND; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.LABEL; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.LEFT_HAND_SIDE; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.LEFT_OPERAND; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.LEFT_PARENTHESIS; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.MODIFIERS; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.NAME; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.NATIVE_CHILDREN; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.NATIVE_KIND; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.OPERAND; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.OPERATOR; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.OPERATOR_TOKEN; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.PLACE_HOLDER_TOKEN; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.RETURN_TYPE; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.RIGHT_OPERAND; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.RIGHT_PARENTHESIS; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.STATEMENT_OR_EXPRESSION; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.STATEMENT_OR_EXPRESSIONS; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.THEN_BRANCH; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TRY_BLOCK; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TRY_KEYWORD; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TYPE; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.VALUE; + +class JsonTreeTest extends JsonTestHelper { + + @Test + void assignment_expression() throws IOException { + Token tokenIdentifier = otherToken(1, 0, "x"); + otherToken(1, 2, "="); + Token tokenLiteral = otherToken(1, 4, "2"); + + Tree identifier = new IdentifierTreeImpl(metaData(tokenIdentifier), tokenIdentifier.text()); + AssignmentExpressionTree.Operator operator = AssignmentExpressionTree.Operator.EQUAL; + Tree literal = new IntegerLiteralTreeImpl(metaData(tokenLiteral), tokenLiteral.text()); + + TreeMetaData metaData = metaData(tokenIdentifier, tokenLiteral); + AssignmentExpressionTree initialAssignment = new AssignmentExpressionTreeImpl(metaData, operator, identifier, literal); + AssignmentExpressionTree assignment = checkJsonSerializationDeserialization(initialAssignment, "assignment_expression.json"); + assertThat(assignment.leftHandSide().textRange()).isEqualTo(tokenIdentifier.textRange()); + assertThat(assignment.operator()).isEqualTo(AssignmentExpressionTree.Operator.EQUAL); + assertThat(assignment.statementOrExpression().textRange()).isEqualTo(tokenLiteral.textRange()); + + assertThat(methodNames(AssignmentExpressionTree.class)) + .containsExactlyInAnyOrder(OPERATOR, LEFT_HAND_SIDE, STATEMENT_OR_EXPRESSION); + } + + @Test + void binary_expression() throws IOException { + Token tokenX = otherToken(1, 0, "x"); + Token tokenLess = otherToken(1, 2, "<"); + Token tokenY = otherToken(1, 4, "y"); + + Tree identifierX = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + Tree identifierY = new IdentifierTreeImpl(metaData(tokenY), tokenY.text()); + TreeMetaData metaData = metaData(tokenX, tokenY); + BinaryExpressionTree initialExpression = new BinaryExpressionTreeImpl(metaData, BinaryExpressionTree.Operator.LESS_THAN, tokenLess, identifierX, identifierY); + BinaryExpressionTree expression = checkJsonSerializationDeserialization(initialExpression, "binary_expression.json"); + assertThat(expression.leftOperand().textRange()).isEqualTo(tokenX.textRange()); + assertThat(expression.operator()).isEqualTo(BinaryExpressionTree.Operator.LESS_THAN); + assertThat(expression.operatorToken().text()).isEqualTo("<"); + assertThat(expression.rightOperand().textRange()).isEqualTo(tokenY.textRange()); + + assertThat(methodNames(BinaryExpressionTree.class)) + .containsExactlyInAnyOrder(OPERATOR, OPERATOR_TOKEN, LEFT_OPERAND, RIGHT_OPERAND); + } + + @Test + void block() throws IOException { + Token tokenOpen = otherToken(1, 0, "{"); + Token tokenX = otherToken(1, 2, "x"); + Token tokenClose = otherToken(1, 4, "}"); + Tree identifierX = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + TreeMetaData metaData = metaData(tokenOpen, tokenClose); + BlockTree initialBlock = new BlockTreeImpl(metaData, singletonList(identifierX)); + BlockTree block = checkJsonSerializationDeserialization(initialBlock, "block.json"); + assertThat(block.children()).hasSize(1); + assertThat(block.children().get(0).textRange()).isEqualTo(identifierX.textRange()); + assertThat(block.textRange()).isEqualTo(metaData.textRange()); + + assertThat(methodNames(BlockTree.class)) + .containsExactlyInAnyOrder(STATEMENT_OR_EXPRESSIONS); + } + + @Test + void catch_tree() throws IOException { + Token tokenCatch = keywordToken(1, 0, "catch"); + Token tokenX = otherToken(1, 8, "x"); + Token tokenY = otherToken(1, 10, "y"); + Tree identifierX = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + Tree identifierY = new IdentifierTreeImpl(metaData(tokenY), tokenY.text()); + TreeMetaData metaData = metaData(tokenCatch, tokenY); + CatchTree initialCatch = new CatchTreeImpl(metaData, identifierX, identifierY, tokenCatch); + CatchTree catchTree = checkJsonSerializationDeserialization(initialCatch, "catch_tree.json"); + assertThat(catchTree.catchParameter().textRange()).isEqualTo(identifierX.textRange()); + assertThat(catchTree.catchBlock().textRange()).isEqualTo(identifierY.textRange()); + assertThat(catchTree.keyword().text()).isEqualTo("catch"); + + assertThat(methodNames(CatchTree.class)) + .containsExactlyInAnyOrder(CATCH_PARAMETER, CATCH_BLOCK, KEYWORD); + } + + @Test + void catch_tree_without_parameter() throws IOException { + Token tokenCatch = keywordToken(1, 0, "catch"); + Token tokenY = otherToken(1, 10, "y"); + Tree identifierY = new IdentifierTreeImpl(metaData(tokenY), tokenY.text()); + TreeMetaData metaData = metaData(tokenCatch, tokenY); + CatchTree initialCatch = new CatchTreeImpl(metaData, null, identifierY, tokenCatch); + CatchTree catchTree = checkJsonSerializationDeserialization(initialCatch, "catch_tree_without_parameter.json"); + assertThat(catchTree.catchParameter()).isNull(); + assertThat(catchTree.catchBlock().textRange()).isEqualTo(identifierY.textRange()); + assertThat(catchTree.keyword().text()).isEqualTo("catch"); + } + + @Test + void class_declaration() throws IOException { + Token tokenClass = keywordToken(1, 0, "class"); + Token tokenA = otherToken(1, 6, "A"); + otherToken(1, 8, "{"); + Token tokenClose = otherToken(1, 10, "}"); + IdentifierTree identifierA = new IdentifierTreeImpl(metaData(tokenA), tokenA.text()); + NativeTree nativeTree = new NativeTreeImpl(metaData(tokenA, tokenClose), StringNativeKind.of("CLASS"), singletonList(identifierA)); + ClassDeclarationTree initialTree = new ClassDeclarationTreeImpl(metaData(tokenClass, tokenClose), identifierA, nativeTree); + ClassDeclarationTree tree = checkJsonSerializationDeserialization(initialTree, "class_declaration.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:11 - class A { }"); + assertThat(tokens(tree.identifier())).isEqualTo("1:6:1:7 - A"); + assertThat(tokens(tree.classTree())).isEqualTo("1:6:1:11 - A { }"); + + assertThat(methodNames(ClassDeclarationTree.class)) + .containsExactlyInAnyOrder(IDENTIFIER, CLASS_TREE); + } + + @Test + void class_declaration_anonymous() throws IOException { + Token tokenClass = keywordToken(1, 0, "class"); + Token tokenOpen = otherToken(1, 8, "{"); + Token tokenClose = otherToken(1, 10, "}"); + NativeTree nativeTree = new NativeTreeImpl(metaData(tokenOpen, tokenClose), StringNativeKind.of("CLASS"), emptyList()); + ClassDeclarationTree initialTree = new ClassDeclarationTreeImpl(metaData(tokenClass, tokenClose), null, nativeTree); + ClassDeclarationTree tree = checkJsonSerializationDeserialization(initialTree, "class_declaration_anonymous.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:11 - class { }"); + assertThat(tree.identifier()).isNull(); + assertThat(tokens(tree.classTree())).isEqualTo("1:8:1:11 - { }"); + } + + @Test + void exception_handling() throws IOException { + Token tokenTry = keywordToken(1, 0, "try"); + Token tokenX = otherToken(1, 10, "x"); + Tree identifierX = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + + Token tokenCatch = keywordToken(1, 20, "catch"); + Token tokenY = otherToken(1, 30, "y"); + Tree identifierY = new IdentifierTreeImpl(metaData(tokenY), tokenY.text()); + CatchTree catchTree = new CatchTreeImpl(metaData(tokenCatch, tokenY), null, identifierY, tokenCatch); + + keywordToken(1, 40, "finally"); + Token tokenZ = otherToken(1, 50, "z"); + Tree identifierZ = new IdentifierTreeImpl(metaData(tokenZ), tokenZ.text()); + + TreeMetaData metaData = metaData(tokenTry, identifierZ); + ExceptionHandlingTree initialCatch = new ExceptionHandlingTreeImpl(metaData, identifierX, tokenTry, + singletonList(catchTree), identifierZ); + ExceptionHandlingTree tree = checkJsonSerializationDeserialization(initialCatch, "exception_handling.json"); + assertThat(tree.textRange()).isEqualTo(metaData.textRange()); + assertThat(tree.tryBlock().textRange()).isEqualTo(identifierX.textRange()); + assertThat(tree.tryKeyword().text()).isEqualTo("try"); + assertThat(tree.catchBlocks()).hasSize(1); + assertThat(tree.catchBlocks().get(0)).isInstanceOf(CatchTree.class); + assertThat(tree.catchBlocks().get(0).keyword().text()).isEqualTo("catch"); + assertThat(tree.finallyBlock()).isNotNull(); + assertThat(tree.finallyBlock().textRange()).isEqualTo(identifierZ.textRange()); + + assertThat(methodNames(ExceptionHandlingTree.class)) + .containsExactlyInAnyOrder(TRY_BLOCK, TRY_KEYWORD, CATCH_BLOCKS, FINALLY_BLOCK); + } + + @Test + void exception_handling_without_catch() throws IOException { + Token tokenTry = keywordToken(1, 0, "try"); + Token tokenX = otherToken(1, 10, "x"); + Tree identifierX = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + TreeMetaData metaData = metaData(tokenTry, tokenX); + ExceptionHandlingTree initialCatch = new ExceptionHandlingTreeImpl(metaData, identifierX, tokenTry, emptyList(), null); + ExceptionHandlingTree tree = checkJsonSerializationDeserialization(initialCatch, "exception_handling_without_catch.json"); + assertThat(tree.textRange()).isEqualTo(metaData.textRange()); + assertThat(tree.tryBlock().textRange()).isEqualTo(identifierX.textRange()); + assertThat(tree.tryKeyword().text()).isEqualTo("try"); + assertThat(tree.catchBlocks()).isEmpty(); + assertThat(tree.finallyBlock()).isNull(); + } + + @Test + void function_declaration() throws IOException { + Token tokenPublic = keywordToken(1, 0, "public"); + NativeTree modifier = new NativeTreeImpl(metaData(tokenPublic), StringNativeKind.of("modifier"), emptyList()); + List modifiers = singletonList(modifier); + boolean isConstructor = true; + Token tokenInt = otherToken(1, 7, "int"); + Tree returnType = new IdentifierTreeImpl(metaData(tokenInt), tokenInt.text()); + Token tokenName = otherToken(1, 11, "foo"); + IdentifierTree name = new IdentifierTreeImpl(metaData(tokenName), tokenName.text()); + Token tokenParam = otherToken(1, 15, "param"); + Tree param = new IdentifierTreeImpl(metaData(tokenParam), tokenParam.text()); + List parameters = singletonList(param); + Token tokenOpen = otherToken(1, 20, "{"); + Token tokenClose = otherToken(1, 22, "}"); + BlockTree body = new BlockTreeImpl(metaData(tokenOpen, tokenClose), emptyList()); + Token tokenNative = keywordToken(1, 24, "->"); + List nativeChildren = singletonList(new NativeTreeImpl(metaData(tokenNative), StringNativeKind.of("arrow"), emptyList())); + TreeMetaData metaData = metaData(tokenPublic, tokenNative); + FunctionDeclarationTree initialFunction = new FunctionDeclarationTreeImpl(metaData, modifiers, isConstructor, returnType, name, parameters, body, nativeChildren); + FunctionDeclarationTree function = checkJsonSerializationDeserialization(initialFunction, "function_declaration.json"); + assertThat(function.textRange()).isEqualTo(metaData.textRange()); + assertThat(function.modifiers()).hasSize(1); + assertThat(function.modifiers().get(0).textRange()).isEqualTo(modifier.textRange()); + assertThat(function.isConstructor()).isTrue(); + assertThat(function.returnType().textRange()).isEqualTo(tokenInt.textRange()); + assertThat(function.name().textRange()).isEqualTo(tokenName.textRange()); + assertThat(function.formalParameters()).hasSize(1); + assertThat(function.formalParameters().get(0).textRange()).isEqualTo(tokenParam.textRange()); + assertThat(function.body().textRange()).isEqualTo(body.textRange()); + assertThat(function.nativeChildren()).hasSize(1); + assertThat(function.nativeChildren().get(0).textRange()).isEqualTo(tokenNative.textRange()); + + assertThat(methodNames(FunctionDeclarationTree.class)) + .containsExactlyInAnyOrder(MODIFIERS, IS_CONSTRUCTOR, RETURN_TYPE, NAME, FORMAL_PARAMETERS, BODY, NATIVE_CHILDREN, "rangeToHighlight"); + } + + @Test + void identifier() throws IOException { + Token token = otherToken(1, 0, "foo"); + IdentifierTree initialIdentifier = new IdentifierTreeImpl(metaData(token), token.text()); + IdentifierTree identifier = checkJsonSerializationDeserialization(initialIdentifier, "identifier.json"); + assertThat(identifier.name()).isEqualTo("foo"); + assertThat(identifier.identifier()).isEqualTo("foo"); + assertThat(identifier.textRange()).isEqualTo(token.textRange()); + + assertThat(methodNames(IdentifierTree.class)) + .containsExactlyInAnyOrder(NAME, "identifier"); + } + + @Test + void if_tree() throws IOException { + Token tokenIf = keywordToken(1, 0, "if"); + Token tokenTrue = otherToken(1, 3, "true"); + Tree condition = new LiteralTreeImpl(metaData(tokenTrue), tokenTrue.text()); + + Token tokenX = otherToken(1, 8, "x"); + Tree thenBranch = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + + Token tokenElse = otherToken(1, 10, "else"); + Token tokenY = otherToken(1, 15, "y"); + Tree elseBranch = new IdentifierTreeImpl(metaData(tokenY), tokenY.text()); + + IfTree initialTree = new IfTreeImpl(metaData(tokenIf, tokenY), condition, thenBranch, elseBranch, tokenIf, tokenElse); + IfTree tree = checkJsonSerializationDeserialization(initialTree, "if_tree.json"); + assertThat(tree.ifKeyword().text()).isEqualTo("if"); + assertThat(tree.thenBranch().textRange()).isEqualTo(thenBranch.textRange()); + assertThat(tree.elseKeyword().text()).isEqualTo("else"); + assertThat(tree.elseBranch().textRange()).isEqualTo(elseBranch.textRange()); + + assertThat(methodNames(IfTree.class)) + .containsExactlyInAnyOrder(CONDITION, THEN_BRANCH, ELSE_BRANCH, IF_KEYWORD, ELSE_KEYWORD); + } + + @Test + void if_tree_without_else() throws IOException { + Token tokenIf = keywordToken(1, 0, "if"); + Token tokenTrue = otherToken(1, 3, "true"); + Tree condition = new LiteralTreeImpl(metaData(tokenTrue), tokenTrue.text()); + + Token tokenX = otherToken(1, 8, "x"); + Tree thenBranch = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + + Token tokenElse = null; + Tree elseBranch = null; + IfTree initialTree = new IfTreeImpl(metaData(tokenIf, tokenX), condition, thenBranch, elseBranch, tokenIf, tokenElse); + IfTree tree = checkJsonSerializationDeserialization(initialTree, "if_tree_without_else.json"); + assertThat(tree.ifKeyword().text()).isEqualTo("if"); + assertThat(tree.thenBranch().textRange()).isEqualTo(thenBranch.textRange()); + assertThat(tree.elseKeyword()).isNull(); + assertThat(tree.elseBranch()).isNull(); + } + + @Test + void import_declaration() throws IOException { + Token tokenImport = keywordToken(1, 0, "import"); + Token tokenLib = otherToken(1, 7, "lib"); + Tree lib = new IdentifierTreeImpl(metaData(tokenLib), tokenLib.text()); + ImportDeclarationTree initialTree = new ImportDeclarationTreeImpl(metaData(tokenImport, tokenLib), singletonList(lib)); + ImportDeclarationTree tree = checkJsonSerializationDeserialization(initialTree, "import_declaration.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:10 - import lib"); + assertThat(tree.children()).hasSize(1); + assertThat(tokens(tree.children().get(0))).isEqualTo("1:7:1:10 - lib"); + + assertThat(methodNames(ImportDeclarationTree.class)) + .isEmpty(); + } + + @Test + void integer_literal() throws IOException { + Token token = otherToken(1, 0, "0xFF"); + IntegerLiteralTree initialLiteral = new IntegerLiteralTreeImpl(metaData(token), token.text()); + IntegerLiteralTree literal = checkJsonSerializationDeserialization(initialLiteral, "integer_literal.json"); + assertThat(literal.value()).isEqualTo("0xFF"); + assertThat(literal.getBase()).isEqualTo(IntegerLiteralTree.Base.HEXADECIMAL); + assertThat(literal.getNumericPart()).isEqualTo("FF"); + assertThat(literal.getIntegerValue()).isEqualTo(BigInteger.valueOf(255)); + + assertThat(methodNames(IntegerLiteralTree.class)) + .containsExactlyInAnyOrder(VALUE, "getBase", "getIntegerValue", "getNumericPart"); + } + + @Test + void jump() throws IOException { + Token tokenKeyword = keywordToken(1, 0, "break"); + Token tokenLabel = otherToken(1, 6, "hard"); + IdentifierTree label = new IdentifierTreeImpl(metaData(tokenLabel), tokenLabel.text()); + JumpTree.JumpKind kind = JumpTree.JumpKind.BREAK; + JumpTree initialTree = new JumpTreeImpl(metaData(tokenKeyword, tokenLabel), tokenKeyword, kind, label); + JumpTree tree = checkJsonSerializationDeserialization(initialTree, "jump.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:10 - break hard"); + assertThat(tokens(tree.label())).isEqualTo("1:6:1:10 - hard"); + assertThat(tree.kind()).isEqualTo(JumpTree.JumpKind.BREAK); + assertThat(token(tree.keyword())).isEqualTo("1:0:1:5 - break"); + + assertThat(methodNames(JumpTree.class)) + .containsExactlyInAnyOrder(LABEL, KEYWORD, KIND); + } + + @Test + void literal() throws IOException { + Token token = otherToken(1, 0, "true"); + LiteralTree initialLiteral = new LiteralTreeImpl(metaData(token), token.text()); + LiteralTree literal = checkJsonSerializationDeserialization(initialLiteral, "literal.json"); + assertThat(literal.value()).isEqualTo("true"); + TreeMetaData metaData = literal.metaData(); + assertThat(metaData.textRange()).isEqualTo(token.textRange()); + assertThat(metaData.linesOfCode()).containsExactly(1); + assertThat(metaData.commentsInside()).isEmpty(); + assertThat(metaData.tokens()).hasSize(1); + Token metaDataToken = metaData.tokens().get(0); + assertThat(metaDataToken.text()).isEqualTo("true"); + assertThat(metaDataToken.type()).isEqualTo(Token.Type.OTHER); + assertThat(metaDataToken.textRange()).isEqualTo(token.textRange()); + + assertThat(methodNames(LiteralTree.class)) + .containsExactlyInAnyOrder(VALUE); + } + + @Test + void loop() throws IOException { + Token tokenSwitch = keywordToken(1, 0, "while"); + Token tokenTrue = otherToken(1, 6, "true"); + Token tokenX = keywordToken(1, 11, "x"); + Tree condition = new LiteralTreeImpl(metaData(tokenTrue), tokenTrue.text()); + Tree body = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + LoopTree initialTree = new LoopTreeImpl(metaData(tokenSwitch, tokenX), condition, body, LoopTree.LoopKind.WHILE, tokenSwitch); + LoopTree tree = checkJsonSerializationDeserialization(initialTree, "loop.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:12 - while true x"); + assertThat(tokens(tree.condition())).isNotNull(); + assertThat(tokens(tree.condition())).isEqualTo("1:6:1:10 - true"); + assertThat(tokens(tree.body())).isEqualTo("1:11:1:12 - x"); + assertThat(tree.kind()).isEqualTo(LoopTree.LoopKind.WHILE); + assertThat(token(tree.keyword())).isEqualTo("1:0:1:5 - while"); + + assertThat(methodNames(LoopTree.class)) + .containsExactlyInAnyOrder(CONDITION, BODY, KIND, KEYWORD); + } + + @Test + void match() throws IOException { + Token tokenSwitch = keywordToken(1, 0, "switch"); + Token tokenX = otherToken(1, 7, "x"); + Token tokenDefault = keywordToken(1, 9, "default"); + Token tokenValue = otherToken(1, 17, "42"); + + Tree expression = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + Tree value = new IntegerLiteralTreeImpl(metaData(tokenValue), tokenValue.text()); + MatchCaseTree matchCase = new MatchCaseTreeImpl(metaData(tokenDefault, tokenValue), null, value); + MatchTree initialTree = new MatchTreeImpl(metaData(tokenSwitch, tokenValue), expression, singletonList(matchCase), tokenSwitch); + MatchTree tree = checkJsonSerializationDeserialization(initialTree, "match.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:19 - switch x default 42"); + assertThat(tokens(tree.expression())).isEqualTo("1:7:1:8 - x"); + assertThat(tree.cases()).hasSize(1); + assertThat(tokens(tree.cases().get(0))).isEqualTo("1:9:1:19 - default 42"); + assertThat(token(tree.keyword())).isEqualTo("1:0:1:6 - switch"); + + assertThat(methodNames(MatchTree.class)) + .containsExactlyInAnyOrder(EXPRESSION, CASES, KEYWORD); + } + + @Test + void match_case() throws IOException { + Token tokenCase = keywordToken(1, 0, "case"); + Token tokenSeven = otherToken(1, 5, "7"); + otherToken(1, 7, ":"); + Token tokenX = otherToken(1, 9, "x"); + Tree expression = new IntegerLiteralTreeImpl(metaData(tokenSeven), tokenSeven.text()); + Tree body = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + TreeMetaData metaData = metaData(tokenCase, tokenX); + MatchCaseTree initialTree = new MatchCaseTreeImpl(metaData, expression, body); + MatchCaseTree tree = checkJsonSerializationDeserialization(initialTree, "match_case.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:10 - case 7 : x"); + assertThat(tokens(tree.expression())).isEqualTo("1:5:1:6 - 7"); + assertThat(tokens(tree.body())).isEqualTo("1:9:1:10 - x"); + + assertThat(methodNames(MatchCaseTree.class)) + .containsExactlyInAnyOrder(EXPRESSION, BODY, "rangeToHighlight"); + } + + @Test + void modifier() throws IOException { + Token tokenPublic = keywordToken(1, 0, "public"); + ModifierTree initialTree = new ModifierTreeImpl(metaData(tokenPublic), ModifierTree.Kind.PUBLIC); + ModifierTree tree = checkJsonSerializationDeserialization(initialTree, "modifier.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:6 - public"); + assertThat(tree.kind()).isEqualTo(ModifierTree.Kind.PUBLIC); + + assertThat(methodNames(ModifierTree.class)) + .containsExactlyInAnyOrder(KIND); + } + + @Test + void native_tree() throws IOException { + Token token10 = otherToken(10, 0, "token10"); + Token token21 = otherToken(21, 0, "token21"); + Token token22 = otherToken(22, 0, "token22"); + NativeTree initialTree = new NativeTreeImpl(metaData(token10, token22), StringNativeKind.of("PARENT"), + Arrays.asList( + new NativeTreeImpl(metaData(token21), StringNativeKind.of("CHILD"), emptyList()), + new NativeTreeImpl(metaData(token22), StringNativeKind.of("CHILD"), emptyList()))); + NativeTree tree = checkJsonSerializationDeserialization(initialTree, "native_tree.json"); + assertThat(tree.nativeKind()).hasToString("PARENT"); + assertThat(tree.children()).hasSize(2); + assertThat(((NativeTree) tree.children().get(0)).nativeKind()).hasToString("CHILD"); + assertThat(tokens(tree.children().get(0))).isEqualTo("21:0:21:7 - token21"); + assertThat(tokens(tree.children().get(1))).isEqualTo("22:0:22:7 - token22"); + + assertThat(methodNames(NativeTree.class)) + .containsExactlyInAnyOrder(NATIVE_KIND); + } + + @Test + void package_declaration() throws IOException { + Token tokenPackage = keywordToken(1, 0, "package"); + Token tokenName = otherToken(1, 8, "hello"); + IdentifierTree name = new IdentifierTreeImpl(metaData(tokenName), tokenName.text()); + TreeMetaData metaData = metaData(tokenPackage, tokenName); + PackageDeclarationTree initialTree = new PackageDeclarationTreeImpl(metaData, Collections.singletonList(name)); + PackageDeclarationTree tree = checkJsonSerializationDeserialization(initialTree, "package_declaration.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:13 - package hello"); + assertThat(tree.children()).hasSize(1); + assertThat(tokens(tree.children().get(0))).isEqualTo("1:8:1:13 - hello"); + + assertThat(methodNames(PackageDeclarationTree.class)) + .isEmpty(); + } + + @Test + void parameter() throws IOException { + Token tokenMod = otherToken(1, 0, "@Nullable"); + Token tokenX = otherToken(2, 0, "x"); + Token tokenInt = otherToken(2, 2, "int"); + Token tokenValue = otherToken(2, 6, "42"); + + IdentifierTree modifier = new IdentifierTreeImpl(metaData(tokenMod), tokenMod.text()); + IdentifierTree identifier = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + Tree type = new IdentifierTreeImpl(metaData(tokenInt), tokenInt.text()); + Tree defaultValue = new IntegerLiteralTreeImpl(metaData(tokenValue), tokenValue.text()); + + TreeMetaData metaData = metaData(tokenMod, tokenValue); + ParameterTree initialTree = new ParameterTreeImpl(metaData, identifier, type, defaultValue, Collections.singletonList(modifier)); + ParameterTree tree = checkJsonSerializationDeserialization(initialTree, "parameter.json"); + assertThat(tokens(tree)).isEqualTo("1:0:2:8 - @Nullable x int 42"); + assertThat(tokens(tree.identifier())).isEqualTo("2:0:2:1 - x"); + assertThat(tokens(tree.type())).isEqualTo("2:2:2:5 - int"); + assertThat(tokens(tree.defaultValue())).isEqualTo("2:6:2:8 - 42"); + assertThat(tree.modifiers()).hasSize(1); + assertThat(tokens(tree.modifiers().get(0))).isEqualTo("1:0:1:9 - @Nullable"); + + assertThat(methodNames(ParameterTree.class)) + .containsExactlyInAnyOrder(IDENTIFIER, TYPE, DEFAULT_VALUE, MODIFIERS); + } + + @Test + void parenthesized_expression() throws IOException { + Token leftParenthesis = otherToken(1, 0, "("); + Token tokenX = otherToken(1, 1, "x"); + Token rightParenthesis = otherToken(1, 2, ")"); + Tree expression = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + TreeMetaData metaData = metaData(leftParenthesis, rightParenthesis); + ParenthesizedExpressionTree initialTree = new ParenthesizedExpressionTreeImpl(metaData, expression, leftParenthesis, rightParenthesis); + ParenthesizedExpressionTree tree = checkJsonSerializationDeserialization(initialTree, "parenthesized_expression.json"); + assertThat(tokens(tree)).isEqualTo("1:0:1:3 - ( x )"); + assertThat(tokens(tree.expression())).isEqualTo("1:1:1:2 - x"); + assertThat(token(tree.leftParenthesis())).isEqualTo("1:0:1:1 - ("); + assertThat(token(tree.rightParenthesis())).isEqualTo("1:2:1:3 - )"); + + assertThat(methodNames(ParenthesizedExpressionTree.class)) + .containsExactlyInAnyOrder(EXPRESSION, LEFT_PARENTHESIS, RIGHT_PARENTHESIS); + } + + @Test + void place_holder() throws IOException { + Token token = keywordToken(1, 0, "_"); + PlaceHolderTree initialTree = new PlaceHolderTreeImpl(metaData(token), token); + PlaceHolderTree tree = checkJsonSerializationDeserialization(initialTree, "place_holder.json"); + assertThat(tree.textRange()).isEqualTo(token.textRange()); + assertThat(tree.placeHolderToken().textRange()).isEqualTo(token.textRange()); + assertThat(tree.placeHolderToken().text()).isEqualTo("_"); + + assertThat(methodNames(PlaceHolderTree.class)) + .containsExactlyInAnyOrder(PLACE_HOLDER_TOKEN, IDENTIFIER, NAME); + } + + @Test + void return_true() throws IOException { + Token returnToken = keywordToken(1, 0, "return"); + Token trueToken = otherToken(1, 7, "true"); + Token semicolonToken = otherToken(1, 11, ";"); + Tree trueLiteral = new LiteralTreeImpl(metaData(trueToken), trueToken.text()); + ReturnTree initialReturnTree = new ReturnTreeImpl(metaData(returnToken, semicolonToken), returnToken, trueLiteral); + ReturnTree returnTree = checkJsonSerializationDeserialization(initialReturnTree, "return_true.json"); + assertThat(returnTree.keyword().text()).isEqualTo("return"); + assertThat(returnTree.body()).isInstanceOf(LiteralTree.class); + TreeMetaData metaData = returnTree.metaData(); + assertThat(metaData.textRange()).isEqualTo(new TextRangeImpl(1, 0, 1, 12)); + assertThat(metaData.linesOfCode()).containsExactly(1); + assertThat(metaData.commentsInside()).isEmpty(); + assertThat(metaData.tokens()).hasSize(3); + + assertThat(methodNames(ReturnTree.class)) + .containsExactlyInAnyOrder(BODY, KEYWORD); + } + + @Test + void string_literal() throws IOException { + Token token = stringToken(1, 0, "\"a\""); + StringLiteralTree initialLiteral = new StringLiteralTreeImpl(metaData(token), token.text()); + StringLiteralTree literal = checkJsonSerializationDeserialization(initialLiteral, "string_literal.json"); + assertThat(literal.value()).isEqualTo("\"a\""); + assertThat(literal.content()).isEqualTo("a"); + TreeMetaData metaData = literal.metaData(); + assertThat(metaData.textRange()).isEqualTo(token.textRange()); + assertThat(metaData.linesOfCode()).containsExactly(1); + assertThat(metaData.commentsInside()).isEmpty(); + assertThat(metaData.tokens()).hasSize(1); + Token metaDataToken = metaData.tokens().get(0); + assertThat(metaDataToken.text()).isEqualTo("\"a\""); + assertThat(metaDataToken.type()).isEqualTo(Token.Type.STRING_LITERAL); + assertThat(metaDataToken.textRange()).isEqualTo(token.textRange()); + + assertThat(methodNames(StringLiteralTree.class)) + .containsExactlyInAnyOrder(CONTENT, VALUE); + } + + @Test + void throw_tree() throws IOException { + Token tokenThrow = keywordToken(1, 0, "throw"); + Token tokenEx = otherToken(1, 6, "ex"); + Tree body = new IdentifierTreeImpl(metaData(tokenEx), tokenEx.text()); + ThrowTree initialTree = new ThrowTreeImpl(metaData(tokenThrow, tokenEx), tokenThrow, body); + ThrowTree tree = checkJsonSerializationDeserialization(initialTree, "throw_tree.json"); + assertThat(tree.keyword().text()).isEqualTo("throw"); + assertThat(tree.body().textRange()).isEqualTo(tokenEx.textRange()); + + assertThat(methodNames(ThrowTree.class)) + .containsExactlyInAnyOrder(KEYWORD, BODY); + } + + @Test + void throw_nothing() throws IOException { + Token tokenThrow = keywordToken(1, 0, "throw"); + ThrowTree initialTree = new ThrowTreeImpl(metaData(tokenThrow), tokenThrow, null); + ThrowTree tree = checkJsonSerializationDeserialization(initialTree, "throw_nothing.json"); + assertThat(tree.keyword().text()).isEqualTo("throw"); + assertThat(tree.body()).isNull(); + } + + @Test + void top_level() throws IOException { + Comment comment = comment(1, 0, "// hello", 2, 0); + Token token = otherToken(2, 0, "true"); + Tree trueLiteral = new LiteralTreeImpl(metaData(token), token.text()); + TreeMetaData metaData = metaData(comment, token); + TopLevelTree initialTree = new TopLevelTreeImpl(metaData, singletonList(trueLiteral), singletonList(comment), token); + TopLevelTree tree = checkJsonSerializationDeserialization(initialTree, "top_level.json"); + assertThat(tree.allComments()).hasSize(1); + assertThat(tree.allComments().get(0).text()).isEqualTo("// hello"); + assertThat(tree.declarations()).hasSize(1); + assertThat(tree.declarations().get(0).metaData().textRange()).isEqualTo(token.textRange()); + assertThat(tree.firstCpdToken().text()).isEqualTo("true"); + + assertThat(methodNames(TopLevelTree.class)) + .containsExactlyInAnyOrder(DECLARATIONS, FIRST_CPD_TOKEN, "allComments"); + } + + @Test + void unary_expression() throws IOException { + Token tokenMinus = otherToken(1, 0, "-"); + Token tokenX = otherToken(1, 1, "x"); + UnaryExpressionTree.Operator operator = UnaryExpressionTree.Operator.MINUS; + Tree operand = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + UnaryExpressionTree initialTree = new UnaryExpressionTreeImpl(metaData(tokenMinus, tokenX), operator, operand); + UnaryExpressionTree tree = checkJsonSerializationDeserialization(initialTree, "unary_expression.json"); + assertThat(tree.operator()).isEqualTo(UnaryExpressionTree.Operator.MINUS); + assertThat(tree.operand().textRange()).isEqualTo(operand.textRange()); + + assertThat(methodNames(UnaryExpressionTree.class)) + .containsExactlyInAnyOrder(OPERATOR, OPERAND); + } + + @Test + void variable_declaration() throws IOException { + Token tokenInt = otherToken(1, 0, "int"); + Token tokenX = otherToken(1, 4, "x"); + Token tokenValue = otherToken(1, 6, "42"); + IdentifierTree identifier = new IdentifierTreeImpl(metaData(tokenX), tokenX.text()); + IdentifierTree type = new IdentifierTreeImpl(metaData(tokenInt), tokenInt.text()); + Tree initializer = new IntegerLiteralTreeImpl(metaData(tokenValue), tokenValue.text()); + VariableDeclarationTree initialTree = new VariableDeclarationTreeImpl( + metaData(tokenInt, tokenValue), identifier, type, initializer, true); + VariableDeclarationTree tree = checkJsonSerializationDeserialization(initialTree, "variable_declaration.json"); + assertThat(tree.identifier().name()).isEqualTo("x"); + assertThat(((IdentifierTree) tree.type()).name()).isEqualTo("int"); + assertThat(((IntegerLiteralTree) tree.initializer()).getIntegerValue()).isEqualTo(BigInteger.valueOf(42)); + assertThat(tree.isVal()).isTrue(); + + assertThat(methodNames(VariableDeclarationTree.class)) + .containsExactlyInAnyOrder(IDENTIFIER, TYPE, INITIALIZER, IS_VAL); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/DeserializationContextTest.java b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/DeserializationContextTest.java new file mode 100644 index 00000000..ae888df5 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/DeserializationContextTest.java @@ -0,0 +1,172 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonArray; +import com.eclipsesource.json.JsonObject; +import java.util.Arrays; +import java.util.List; +import java.util.NoSuchElementException; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.persistence.JsonTestHelper; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class DeserializationContextTest extends JsonTestHelper { + + private DeserializationContext context = new DeserializationContext(JsonTreeConverter.POLYMORPHIC_CONVERTER) + .withMetaDataProvider(metaDataProvider); + + @Test + void resolve_token() { + Token token = otherToken(1, 0, "foo"); + JsonObject json = Json.object() + .add("tokenReference", "1:0:1:3"); + + assertThat(context.fieldToToken(json, "tokenReference")).isSameAs(token); + assertThat(context.fieldToNullableToken(json, "tokenReference")).isSameAs(token); + assertThat(context.fieldToNullableToken(json, "unknown")).isNull(); + } + + @Test + void resolve_token_not_found() { + otherToken(1, 0, "foo"); + JsonObject json = Json.object() + .add("tokenReference", "7:13:7:20"); + + NoSuchElementException e = assertThrows(NoSuchElementException.class, + () -> context.fieldToToken(json, "tokenReference")); + assertThat(e).hasMessage("Token not found: 7:13:7:20"); + } + + @Test + void resolve_metadata() { + Token token = otherToken(1, 0, "foo"); + JsonObject json = Json.object() + .add("metaData", "1:0:1:3"); + + assertThat(context.metaData(json)).isNotNull(); + assertThat(context.metaData(json).textRange()).isEqualTo(token.textRange()); + } + + @Test + void resolve_metadata_not_found() { + JsonObject json = Json.object() + .add("field1", "42"); + + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> context.metaData(json)); + assertThat(e).hasMessage("Missing non-null value for field 'metaData' at '' member: {\"field1\":\"42\"}"); + } + + @Test + void object_list() { + List nodes = Arrays.asList("A", "B", "C"); + JsonArray array = Json.array(); + nodes.stream().map(value -> Json.object().add("value", value)).forEach(array::add); + + List actual = context.objectList(array, (ctx, object) -> object.getString("value", null)); + assertThat(actual).containsExactly("A", "B", "C"); + + assertThat(context.objectList(null, (ctx, object) -> object)).isEmpty(); + assertThat(context.objectList(Json.NULL, (ctx, object) -> object)).isEmpty(); + } + + @Test + void invalid_object_list() { + context.pushPath("root"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> context.objectList(Json.value(42), (ctx, object) -> object)); + assertThat(e).hasMessage("Expect Array instead of JsonNumber at 'root' member: 42"); + } + + @Test + void field_to_nullable_string() { + JsonObject json = Json.object() + .add("field1", "abc"); + assertThat(context.fieldToNullableString(json, "field1")).isEqualTo("abc"); + assertThat(context.fieldToNullableString(json, "field2")).isNull(); + } + + @Test + void field_to_nullable_invalid_string() { + JsonObject json = Json.object() + .add("field1", 42); + context.pushPath("root"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> context.fieldToNullableString(json, "field1")); + assertThat(e).hasMessage("Expect String instead of 'JsonNumber' for field 'field1' at 'root' member: {\"field1\":42}"); + } + + @Test + void field_to_string() { + JsonObject json = Json.object() + .add("field1", "abc"); + assertThat(context.fieldToString(json, "field1")).isEqualTo("abc"); + } + + @Test + void field_to_missing_string() { + JsonObject json = Json.object() + .add("field1", "abc"); + context.pushPath("TopLevel"); + context.pushPath("AssignmentExpression"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> context.fieldToString(json, "field2")); + assertThat(e).hasMessage("Missing non-null value for field 'field2' at 'TopLevel/AssignmentExpression' member: {\"field1\":\"abc\"}"); + } + + @Test + void field_to_null_string() { + JsonObject json = Json.object() + .add("field1", Json.NULL); + context.pushPath("TopLevel"); + context.pushPath("AssignmentExpression"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> context.fieldToString(json, "field1")); + assertThat(e).hasMessage("Missing non-null value for field 'field1' at 'TopLevel/AssignmentExpression' member: {\"field1\":null}"); + } + + @Test + void field_to_range() { + JsonObject json = Json.object() + .add("field1", "1:2:3:4"); + TextRange range = context.fieldToRange(json, "field1"); + assertThat(range.start().line()).isEqualTo(1); + assertThat(range.start().lineOffset()).isEqualTo(2); + assertThat(range.end().line()).isEqualTo(3); + assertThat(range.end().lineOffset()).isEqualTo(4); + } + + @Test + void field_to_range_missing() { + JsonObject json = Json.object() + .add("field1", "1:2:3:4"); + context.pushPath("root"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> context.fieldToRange(json, "field2")); + assertThat(e).hasMessage("Missing non-null value for field 'field2' at 'root' member: {\"field1\":\"1:2:3:4\"}"); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/JsonTreeConverterTest.java b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/JsonTreeConverterTest.java new file mode 100644 index 00000000..f0c49131 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/JsonTreeConverterTest.java @@ -0,0 +1,254 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import com.eclipsesource.json.Json; +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.BaseTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.TokenImpl; +import org.sonarsource.slang.impl.TreeMetaDataProvider; +import org.sonarsource.slang.persistence.JsonTestHelper; +import org.sonarsource.slang.persistence.JsonTree; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.COMMENT_FROM_JSON; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.COMMENT_TO_JSON; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CONTENT_RANGE; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.CONTENT_TEXT; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TEXT; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TOKEN_FROM_JSON; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TOKEN_TO_JSON; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TREE_METADATA_PROVIDER_FROM_JSON; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TREE_METADATA_PROVIDER_TO_JSON; +import static org.sonarsource.slang.persistence.conversion.JsonTreeConverter.TYPE; + +class JsonTreeConverterTest extends JsonTestHelper { + + private SerializationContext writeContext = new SerializationContext(JsonTreeConverter.POLYMORPHIC_CONVERTER); + private DeserializationContext readContext = new DeserializationContext(JsonTreeConverter.POLYMORPHIC_CONVERTER); + + @Test + void tree_metadata_provider() throws IOException { + Comment initialComment = comment(1, 0, "// hello", 2, 0); + + TextRange tokenRange = new TextRangeImpl(2, 0, 2, 3); + Token initialToken = new TokenImpl(tokenRange, "fun", Token.Type.KEYWORD); + + TreeMetaDataProvider provider = new TreeMetaDataProvider( + singletonList(initialComment), + singletonList(initialToken)); + + String actual = indentedJson(TREE_METADATA_PROVIDER_TO_JSON.apply(writeContext, provider).toString()); + assertThat(actual).isEqualTo(indentedJsonFromFile("tree_metadata_provider.json")); + + provider = TREE_METADATA_PROVIDER_FROM_JSON.apply(readContext, Json.parse(actual).asObject()); + assertThat(provider.allComments()).hasSize(1); + Comment comment = provider.allComments().get(0); + assertThat(comment.text()).isEqualTo("// hello"); + assertThat(comment.contentText()).isEqualTo(" hello"); + assertThat(comment.textRange()).isEqualTo(initialComment.textRange()); + assertThat(comment.contentRange()).isEqualTo(initialComment.contentRange()); + + assertThat(provider.allTokens()).hasSize(1); + Token token = provider.allTokens().get(0); + assertThat(token.textRange()).isEqualTo(initialToken.textRange()); + assertThat(token.text()).isEqualTo("fun"); + assertThat(token.type()).isEqualTo(Token.Type.KEYWORD); + + assertThat(methodNames(TreeMetaDataProvider.class)) + .containsExactlyInAnyOrder("allComments", "previousToken", "updateTokenType", "firstToken", + "allTokens", "indexOfFirstToken", "keyword"); + } + + @Test + void comment() throws IOException { + Comment initialComment = comment(3, 7, "// hello", 2, 0); + String actual = indentedJson(COMMENT_TO_JSON.apply(writeContext, initialComment).toString()); + assertThat(actual).isEqualTo(indentedJsonFromFile("comment.json")); + Comment comment = COMMENT_FROM_JSON.apply(readContext, Json.parse(actual).asObject()); + assertThat(comment.text()).isEqualTo("// hello"); + assertThat(comment.contentText()).isEqualTo(" hello"); + assertThat(comment.textRange()).isEqualTo(initialComment.textRange()); + assertThat(comment.contentRange()).isEqualTo(initialComment.contentRange()); + + assertThat(methodNames(Comment.class)) + .containsExactlyInAnyOrder(TEXT, CONTENT_TEXT, CONTENT_RANGE); + } + + @Test + void token_other() throws IOException { + Token initialToken = otherToken(3, 7, "foo"); + String actual = indentedJson(TOKEN_TO_JSON.apply(writeContext, initialToken).toString()); + assertThat(actual).isEqualTo(indentedJsonFromFile("token_other.json")); + Token token = TOKEN_FROM_JSON.apply(readContext, Json.parse(actual).asObject()); + assertThat(token.textRange()).isEqualTo(initialToken.textRange()); + assertThat(token.text()).isEqualTo("foo"); + assertThat(token.type()).isEqualTo(Token.Type.OTHER); + + assertThat(methodNames(Token.class)) + .containsExactlyInAnyOrder(TEXT, TYPE); + } + + @Test + void token_keyword() throws IOException{ + Token initialToken = keywordToken(1, 2, "key"); + String actual = indentedJson(TOKEN_TO_JSON.apply(writeContext, initialToken).toString()); + assertThat(actual).isEqualTo(indentedJsonFromFile("token_keyword.json")); + Token token = TOKEN_FROM_JSON.apply(readContext, Json.parse(actual).asObject()); + assertThat(token.textRange()).isEqualTo(initialToken.textRange()); + assertThat(token.text()).isEqualTo("key"); + assertThat(token.type()).isEqualTo(Token.Type.KEYWORD); + } + + @Test + void nativeTree_emptyKind() throws IOException { + TreeMetaData metaData = metaData(otherToken(1, 0, "x")); + IdentifierTreeImpl className = new IdentifierTreeImpl(metaData, "MyClass"); + Tree classDecl = new NativeTreeImpl(metaData, new NativeKind() { + @Override + public String toString() { + return ""; + } + }, Collections.singletonList(className)); + String actual = indentedJson(JsonTree.toJson(classDecl)); + assertThat(actual).isEqualTo(indentedJsonFromFile("native_tree_empty_kind.json")); + } + + @Test + void nativeTree_withKind() throws IOException { + TreeMetaData metaData = metaData(otherToken(1, 0, "x")); + IdentifierTreeImpl className = new IdentifierTreeImpl(metaData, "MyClass"); + Tree classDecl = new NativeTreeImpl(metaData, new NativeKind() { + @Override + public String toString() { + return "kind"; + } + }, Collections.singletonList(className)); + String actual = indentedJson(JsonTree.toJson(classDecl)); + assertThat(actual).isEqualTo(indentedJsonFromFile("native_tree_with_kind.json")); + } + + @Test + void error_missing_type() throws IOException { + String invalidJson = indentedJsonFromFile("error_missing_type.json"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> JsonTree.fromJson(invalidJson)); + assertThat(e).hasMessage("Missing non-null value for field '@type' at 'tree/Return/body'" + + " member: {\"invalid_type\":\"Literal\",\"metaData\":\"1:7:1:11\",\"value\":\"true\"}"); + } + + @Test + void error_invalid_json_tree() throws IOException { + String invalidJson = indentedJsonFromFile("error_invalid_json_tree.json"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> JsonTree.fromJson(invalidJson)); + assertThat(e).hasMessage("Unexpected value for Tree at 'tree/Return/body' member: 1234"); + } + + @Test + void error_invalid_tree_type() throws IOException { + String invalidJson = indentedJsonFromFile("error_invalid_tree_type.json"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> JsonTree.fromJson(invalidJson)); + assertThat(e).hasMessage("Invalid '@type' value at 'tree/Return/body/UnsupportedType' member: UnsupportedType"); + } + + @Test + void error_unsupported_tree_class() throws IOException { + Token token = otherToken(1, 0, "x"); + UnsupportedTree tree = new UnsupportedTree(metaData(token)); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> JsonTree.toJson(tree)); + assertThat(e).hasMessage("Unsupported tree class: org.sonarsource.slang.persistence.conversion.JsonTreeConverterTest$UnsupportedTree"); + } + + @Test + void error_unsupported_implementation_class() throws IOException { + Token token = otherToken(1, 0, "x"); + UnsupportedTree tree = new UnsupportedTree(metaData(token)); + SerializationContext ctx = new SerializationContext(JsonTreeConverter.POLYMORPHIC_CONVERTER); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> ctx.newTypedObject(tree)); + assertThat(e).hasMessage("Unsupported implementation class: org.sonarsource.slang.persistence.conversion.JsonTreeConverterTest$UnsupportedTree"); + } + + class UnsupportedTree extends BaseTreeImpl { + public UnsupportedTree(TreeMetaData metaData) { + super(metaData); + } + + @Override + public List children() { + return emptyList(); + } + } + + @Test + void error_unexpected_match_child_class() throws IOException { + String invalidJson = indentedJsonFromFile("error_unexpected_match_child_class.json"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> JsonTree.fromJson(invalidJson)); + assertThat(e).hasMessage("Unexpected 'org.sonarsource.slang.impl.IntegerLiteralTreeImpl'" + + " type for member 'cases[]' instead of" + + " 'org.sonarsource.slang.api.MatchCaseTree'" + + " at 'tree/Match/cases[]/IntegerLiteral'" + + " member: {\"@type\":\"IntegerLiteral\",\"metaData\":\"1:17:1:19\",\"value\":\"42\"}"); + } + + @Test + void error_unary_expression_without_child() throws IOException { + String invalidJson = indentedJsonFromFile("error_unary_expression_without_child.json"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> JsonTree.fromJson(invalidJson)); + assertThat(e).hasMessage("Unexpected null value for field 'operand' at 'tree/UnaryExpression' member: null"); + } + + @Test + void error_unary_expression_with_null_child() throws IOException { + String invalidJson = indentedJsonFromFile("error_unary_expression_with_null_child.json"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> JsonTree.fromJson(invalidJson)); + assertThat(e).hasMessage("Unexpected null value for field 'operand' at 'tree/UnaryExpression' member: null"); + } + + @Test + void nullable_child_can_be_omitted() throws IOException { + JumpTree jump = (JumpTree) JsonTree.fromJson(indentedJsonFromFile("nullable_child_can_be_omitted.json")); + assertThat(jump.label()).isNull(); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/RangeConverterTest.java b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/RangeConverterTest.java new file mode 100644 index 00000000..4ddf3b70 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/RangeConverterTest.java @@ -0,0 +1,105 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import java.util.NoSuchElementException; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.persistence.JsonTestHelper; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class RangeConverterTest extends JsonTestHelper { + + @Test + void format() { + TextRange initialRange = new TextRangeImpl(3, 7, 4, 12); + String actual = RangeConverter.format(initialRange); + assertThat(actual).isEqualTo("3:7:4:12"); + + assertThat(RangeConverter.format(null)).isNull(); + } + + @Test + void parse() { + TextRange range = RangeConverter.parse("3:7:4:12"); + assertThat(range.start().line()).isEqualTo(3); + assertThat(range.start().lineOffset()).isEqualTo(7); + assertThat(range.end().line()).isEqualTo(4); + assertThat(range.end().lineOffset()).isEqualTo(12); + } + + @Test + void parse_null_string() { + assertThat(RangeConverter.parse(null)).isNull(); + } + + @Test + void parse_invalid_string() { + IllegalArgumentException e = assertThrows(IllegalArgumentException.class, + () -> RangeConverter.parse("12345")); + assertThat(e).hasMessage("Invalid TextRange '12345'"); + } + + @Test + void token_reference() { + Token token = otherToken(3, 7, "foo"); + assertThat(RangeConverter.tokenReference(token)).isEqualTo("3:7::10"); + assertThat(RangeConverter.tokenReference(null)).isNull(); + } + + @Test + void resolve_token() { + Token token = otherToken(1, 0, "foo"); + Token actual = RangeConverter.resolveToken(metaDataProvider, "1:0:1:3"); + assertThat(actual).isSameAs(token); + assertThat(RangeConverter.resolveToken(metaDataProvider, null)).isNull(); + } + + @Test + void resolve_invalid_token() { + otherToken(1, 0, "foo"); + NoSuchElementException e = assertThrows(NoSuchElementException.class, + () -> RangeConverter.resolveToken(metaDataProvider, "2:0:2:3")); + assertThat(e).hasMessage("Token not found: 2:0:2:3"); + } + + @Test + void metadata_reference() { + Token token = otherToken(1,0,"true"); + Tree tree = new LiteralTreeImpl(metaData(token), token.text()); + assertThat(RangeConverter.metaDataReference(tree)).isEqualTo("1:0::4"); + } + + @Test + void resolve_metadata() { + Token token = otherToken(3,5,"true"); + TreeMetaData metaData = RangeConverter.resolveMetaData(metaDataProvider, "3:5:3:9"); + assertThat(metaData).isNotNull(); + assertThat(metaData.textRange()).isEqualTo(token.textRange()); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/StringNativeKindTest.java b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/StringNativeKindTest.java new file mode 100644 index 00000000..928a3c31 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/persistence/conversion/StringNativeKindTest.java @@ -0,0 +1,49 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.persistence.conversion; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class StringNativeKindTest { + + @Test + void constructor() { + assertThat(new StringNativeKind("ast.Element")).isNotNull(); + assertThat(StringNativeKind.of("ast.Element")).isNotNull(); + assertThat(StringNativeKind.of(null)).isNull(); + } + + @Test + void to_string() { + assertThat(new StringNativeKind("ast.Element")).hasToString("ast.Element"); + assertThat(StringNativeKind.of("ast.Element")).hasToString("ast.Element"); + assertThat(StringNativeKind.toString(null)).isNull(); + assertThat(StringNativeKind.toString(new StringNativeKind("ast.Element"))).isEqualTo("ast.Element"); + } + + @Test + void test_equals() { + assertThat(new StringNativeKind("ast.Element")).isEqualTo(new StringNativeKind("ast.Element")); + assertThat(new StringNativeKind("ast.Element")).hasSameHashCodeAs(new StringNativeKind("ast.Element")); + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/utils/LogArgTest.java b/slang-api/src/test/java/org/sonarsource/slang/utils/LogArgTest.java new file mode 100644 index 00000000..88b17ce7 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/utils/LogArgTest.java @@ -0,0 +1,42 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.utils; + +import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.assertj.core.api.Assertions.assertThat; + +class LogArgTest { + + private static Logger LOG = LoggerFactory.getLogger(LogArgTest.class); + + @Test + void to_string() { + AtomicInteger counter = new AtomicInteger(42); + Object arg = LogArg.lazyArg(() -> "counter: " + counter.incrementAndGet()); + assertThat(counter.get()).isEqualTo(42); + LOG.info("Test {}", arg); + assertThat(counter.get()).isEqualTo(43); + assertThat(arg).hasToString("counter: 44"); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/utils/SyntacticEquivalenceTest.java b/slang-api/src/test/java/org/sonarsource/slang/utils/SyntacticEquivalenceTest.java new file mode 100644 index 00000000..480f473a --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/utils/SyntacticEquivalenceTest.java @@ -0,0 +1,192 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.utils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Locale; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree.Operator; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.IdentifierTreeImpl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.api.ModifierTree.Kind.PRIVATE; +import static org.sonarsource.slang.api.ModifierTree.Kind.PUBLIC; +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; +import static org.sonarsource.slang.utils.SyntacticEquivalence.findDuplicatedGroups; +import static org.sonarsource.slang.utils.TreeCreationUtils.assignment; +import static org.sonarsource.slang.utils.TreeCreationUtils.binary; +import static org.sonarsource.slang.utils.TreeCreationUtils.identifier; +import static org.sonarsource.slang.utils.TreeCreationUtils.integerLiteral; +import static org.sonarsource.slang.utils.TreeCreationUtils.literal; +import static org.sonarsource.slang.utils.TreeCreationUtils.loop; +import static org.sonarsource.slang.utils.TreeCreationUtils.placeHolderTree; +import static org.sonarsource.slang.utils.TreeCreationUtils.simpleModifier; +import static org.sonarsource.slang.utils.TreeCreationUtils.simpleNative; +import static org.sonarsource.slang.utils.TreeCreationUtils.value; +import static org.sonarsource.slang.utils.TreeCreationUtils.variable; + +class SyntacticEquivalenceTest { + private static NativeKind KIND = new NativeKind() { + @Override + public boolean equals(Object obj) { + return this == obj; + } + }; + + @Test + void test_equivalence() { + Tree literal1 = integerLiteral("1"); + Tree literal2 = integerLiteral("2"); + assertThat(areEquivalent((Tree) null, null)).isTrue(); + assertThat(areEquivalent(literal1, null)).isFalse(); + assertThat(areEquivalent(null, literal1)).isFalse(); + assertThat(areEquivalent(literal1, literal1)).isTrue(); + assertThat(areEquivalent(literal1, integerLiteral("1"))).isTrue(); + assertThat(areEquivalent(literal1, literal2)).isFalse(); + + Tree identifierA = identifier("a"); + assertThat(areEquivalent(identifierA, identifierA)).isTrue(); + assertThat(areEquivalent(identifierA, identifier("a"))).isTrue(); + assertThat(areEquivalent(identifierA, identifier("b"))).isFalse(); + assertThat(areEquivalent(identifierA, literal1)).isFalse(); + + Tree variableA = variable("a"); + assertThat(areEquivalent(variableA, variableA)).isTrue(); + assertThat(areEquivalent(variableA, variable("a"))).isTrue(); + assertThat(areEquivalent(variableA, variable("b"))).isFalse(); + + Tree valueA = value("a"); + assertThat(areEquivalent(valueA, valueA)).isTrue(); + assertThat(areEquivalent(valueA, value("a"))).isTrue(); + assertThat(areEquivalent(valueA, value("b"))).isFalse(); + assertThat(areEquivalent(valueA, variableA)).isFalse(); + + Tree binaryAEquals1 = binary(Operator.EQUAL_TO, identifierA, literal1); + assertThat(areEquivalent(binaryAEquals1, binaryAEquals1)).isTrue(); + assertThat(areEquivalent(binaryAEquals1, binary(Operator.EQUAL_TO, identifierA, literal1))).isTrue(); + assertThat(areEquivalent(binaryAEquals1, binary(Operator.EQUAL_TO, identifierA, literal2))).isFalse(); + assertThat(areEquivalent(binaryAEquals1, binary(Operator.GREATER_THAN_OR_EQUAL_TO, identifierA, literal1))).isFalse(); + + AssignmentExpressionTree.Operator plusEqualOperator = AssignmentExpressionTree.Operator.PLUS_EQUAL; + Tree assignmentAPlusEqual1 = assignment(plusEqualOperator, identifierA, literal1); + assertThat(areEquivalent(assignmentAPlusEqual1, assignmentAPlusEqual1)).isTrue(); + assertThat(areEquivalent(assignmentAPlusEqual1, assignment(plusEqualOperator, identifierA, literal1))).isTrue(); + assertThat(areEquivalent(assignmentAPlusEqual1, assignment(plusEqualOperator, identifierA, literal2))).isFalse(); + assertThat(areEquivalent(assignmentAPlusEqual1, assignment(AssignmentExpressionTree.Operator.EQUAL, identifierA, literal1))).isFalse(); + assertThat(areEquivalent(assignmentAPlusEqual1, binaryAEquals1)).isFalse(); + + Tree native1 = simpleNative(KIND, Collections.singletonList("@a"), Collections.emptyList()); + assertThat(areEquivalent(native1, native1)).isTrue(); + assertThat(areEquivalent(native1, simpleNative(KIND, Collections.singletonList("@a"), Collections.emptyList()))).isTrue(); + assertThat(areEquivalent(native1, simpleNative(KIND, Arrays.asList("@a", "@b"), Collections.emptyList()))).isFalse(); + assertThat(areEquivalent(native1, simpleNative(KIND, Collections.singletonList("1"), Collections.singletonList(literal1)))).isFalse(); + assertThat(areEquivalent(native1, simpleNative(null, Collections.singletonList("@a"), Collections.emptyList()))).isFalse(); + assertThat(areEquivalent(native1, literal1)).isFalse(); + + Tree native2 = simpleNative(KIND, Collections.singletonList("1"), Collections.singletonList(literal1)); + assertThat(areEquivalent(native2, native1)).isFalse(); + assertThat(areEquivalent(native2, native2)).isTrue(); + + Tree modifier1 = simpleModifier(PRIVATE); + assertThat(areEquivalent(modifier1, modifier1)).isTrue(); + assertThat(areEquivalent(modifier1, simpleModifier(PRIVATE))).isTrue(); + assertThat(areEquivalent(modifier1, simpleModifier(PUBLIC))).isFalse(); + assertThat(areEquivalent(modifier1, literal1)).isFalse(); + + Tree placeHolder1 = placeHolderTree(); + Tree placeHolder2 = placeHolderTree(); + assertThat(areEquivalent(placeHolder1, identifierA)).isFalse(); + assertThat(areEquivalent(placeHolder1, identifier("_"))).isFalse(); + assertThat(areEquivalent(placeHolder1, placeHolder2)).isTrue(); + } + + @Test + void test_equivalence_list() { + List list1 = Arrays.asList(identifier("a"), integerLiteral("2")); + List list2 = Arrays.asList(identifier("a"), integerLiteral("2")); + List list3 = Arrays.asList(identifier("a"), integerLiteral("3")); + List list4 = Collections.singletonList(identifier("a")); + + assertThat(areEquivalent((List) null, null)).isTrue(); + assertThat(areEquivalent(list1, null)).isFalse(); + assertThat(areEquivalent(null, list1)).isFalse(); + assertThat(areEquivalent(list1, list1)).isTrue(); + assertThat(areEquivalent(list1, list2)).isTrue(); + assertThat(areEquivalent(list1, list3)).isFalse(); + assertThat(areEquivalent(list1, list4)).isFalse(); + } + + @Test + void duplicateGroups() { + Tree a1 = identifier("a"); + Tree a2 = identifier("a"); + Tree a3 = a1; + Tree b1 = identifier("b"); + assertThat(findDuplicatedGroups(Arrays.asList(a1, b1, a2, a3))).containsExactly(Arrays.asList(a1, a2, a3)); + assertThat(findDuplicatedGroups(Arrays.asList(a1, b1, null))).isEmpty(); + } + + @Test + void loops() { + Tree condition1 = literal("true"); + Tree condition2 = literal("false"); + Tree body1 = integerLiteral("1"); + Tree body2 = integerLiteral("2"); + + LoopTree loop1 = loop(condition1, body1, LoopTree.LoopKind.WHILE, "while"); + + assertThat(areEquivalent(loop1, loop1)).isTrue(); + assertThat(areEquivalent(loop1, loop(condition1, body1, LoopTree.LoopKind.WHILE, "while"))).isTrue(); + assertThat(areEquivalent(loop1, loop(condition2, body1, LoopTree.LoopKind.WHILE, "while"))).isFalse(); + assertThat(areEquivalent(loop1, loop(condition1, body2, LoopTree.LoopKind.WHILE, "while"))).isFalse(); + assertThat(areEquivalent(loop1, loop(condition1, body1, LoopTree.LoopKind.FOR, "while"))).isFalse(); + assertThat(areEquivalent(loop1, loop(condition1, body1, LoopTree.LoopKind.WHILE, "until"))).isFalse(); + } + + @Test + void test_unique_identifier_equivalence() { + IdentifierTreeImpl id = new CustomIdentifierTreeImpl(null, "abc"); + assertThat(areEquivalent(id, new IdentifierTreeImpl(null, "abc"))).isFalse(); + assertThat(areEquivalent(id, id)).isTrue(); + assertThat(areEquivalent(id, new CustomIdentifierTreeImpl(null, "abc"))).isTrue(); + assertThat(areEquivalent(id, new CustomIdentifierTreeImpl(null, "ABc"))).isTrue(); + assertThat(areEquivalent(id, new CustomIdentifierTreeImpl(null, "ABC"))).isTrue(); + assertThat(areEquivalent(id, new CustomIdentifierTreeImpl(null, "a"))).isFalse(); + } + + class CustomIdentifierTreeImpl extends IdentifierTreeImpl { + CustomIdentifierTreeImpl(TreeMetaData metaData, String name) { + super(metaData, name); + } + + @Override + public String identifier() { + return name().toUpperCase(Locale.ENGLISH); + } + } + +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/utils/TreeCreationUtils.java b/slang-api/src/test/java/org/sonarsource/slang/utils/TreeCreationUtils.java new file mode 100644 index 00000000..7c098a02 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/utils/TreeCreationUtils.java @@ -0,0 +1,179 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.utils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.IntegerLiteralTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.PlaceHolderTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.impl.AssignmentExpressionTreeImpl; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.BlockTreeImpl; +import org.sonarsource.slang.impl.FunctionDeclarationTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.IntegerLiteralTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.LoopTreeImpl; +import org.sonarsource.slang.impl.ModifierTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.PlaceHolderTreeImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.TokenImpl; +import org.sonarsource.slang.impl.TopLevelTreeImpl; +import org.sonarsource.slang.impl.VariableDeclarationTreeImpl; + +import static java.util.Collections.emptyList; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class TreeCreationUtils { + private TreeCreationUtils() { + } + + public static LoopTree loop(Tree condition, Tree body, LoopTree.LoopKind kind, String keyword) { + Token tokenKeyword = new TokenImpl(null, keyword, Token.Type.KEYWORD); + return new LoopTreeImpl(null, condition, body, kind, tokenKeyword); + } + + public static IntegerLiteralTree integerLiteral(String value) { + return new IntegerLiteralTreeImpl(null, value); + } + + public static PlaceHolderTree placeHolderTree() { + return new PlaceHolderTreeImpl(null, null); + } + + public static IntegerLiteralTree integerLiteral(String value, TextRange textRange, String ... tokens) { + return new IntegerLiteralTreeImpl(metaData(textRange, tokens), value); + } + + public static LiteralTree literal(String value) { + return new LiteralTreeImpl(null, value); + } + + public static IdentifierTree identifier(String name) { + return new IdentifierTreeImpl(null, name); + } + + public static IdentifierTree identifier(String name, TextRange textRange, String ... tokens) { + return new IdentifierTreeImpl(metaData(textRange, tokens), name); + } + + public static VariableDeclarationTree variable(String name) { + return new VariableDeclarationTreeImpl(null, identifier(name), null, null, false); + } + + public static VariableDeclarationTree value(String name) { + return new VariableDeclarationTreeImpl(null, identifier(name), null, null, true); + } + + public static BinaryExpressionTree binary(BinaryExpressionTree.Operator operator, Tree leftOperand, Tree rightOperand) { + return new BinaryExpressionTreeImpl(null, operator, null, leftOperand, rightOperand); + } + + public static BinaryExpressionTree binary(BinaryExpressionTree.Operator operator, Tree leftOperand, Tree rightOperand, TextRange textRange, String ... tokens) { + return new BinaryExpressionTreeImpl(metaData(textRange, tokens), operator, new TokenImpl(new TextRangeImpl(1,0,1,0), operator.toString(), null), leftOperand, rightOperand); + } + + public static AssignmentExpressionTree assignment(Tree leftOperand, Tree rightOperand) { + return assignment(AssignmentExpressionTree.Operator.EQUAL, leftOperand, rightOperand); + } + + public static AssignmentExpressionTree assignment(Tree leftOperand, Tree rightOperand, TextRange textRange, String ... tokens) { + return assignment(AssignmentExpressionTree.Operator.EQUAL, leftOperand, rightOperand, textRange, tokens); + } + + public static BlockTree block(List body) { + return new BlockTreeImpl(null, body); + } + + public static BlockTree block(List body, TextRange textRange, String ... tokens) { + return new BlockTreeImpl(metaData(textRange, tokens), body); + } + + public static FunctionDeclarationTree simpleFunction(IdentifierTree name, BlockTree body) { + return new FunctionDeclarationTreeImpl(null, Collections.emptyList(), false, null, name, Collections.emptyList(), body, emptyList()); + } + + public static AssignmentExpressionTree assignment(AssignmentExpressionTree.Operator operator, Tree leftOperand, Tree rightOperand) { + return new AssignmentExpressionTreeImpl(null, operator, leftOperand, rightOperand); + } + + public static AssignmentExpressionTree assignment(AssignmentExpressionTree.Operator operator, Tree leftOperand, Tree rightOperand, TextRange textRange, String ... tokens) { + return new AssignmentExpressionTreeImpl(metaData(textRange, tokens), operator, leftOperand, rightOperand); + } + + public static NativeTree simpleNative(NativeKind kind, List children) { + return new NativeTreeImpl(null, kind, children); + } + + public static NativeTree simpleNative(NativeKind kind, List tokens, List children) { + return new NativeTreeImpl(metaData(tokens), kind, children); + } + + public static ModifierTree simpleModifier(ModifierTree.Kind kind) { + return new ModifierTreeImpl(null, kind); + } + + public static TopLevelTree topLevel(List declarations) { + return new TopLevelTreeImpl(null, declarations, null); + } + + private static TreeMetaData metaData(List tokens) { + TreeMetaData metaData = mock(TreeMetaData.class); + mockTokens(metaData, tokens); + return metaData; + } + + private static TreeMetaData metaData(TextRange textRange, String ... tokens) { + TreeMetaData metaData = mock(TreeMetaData.class); + mockTokens(metaData, Arrays.asList(tokens)); + mockTextRange(metaData, textRange); + return metaData; + } + + private static void mockTokens(TreeMetaData metaData, List tokens) { + when(metaData.tokens()).thenReturn(tokens.stream() + .map(text -> new TokenImpl(null, text, null)) + .collect(Collectors.toList())); + } + + private static void mockTextRange(TreeMetaData metaData, TextRange textRange) { + when(metaData.textRange()).thenReturn(textRange); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/visitors/TreePrinterTest.java b/slang-api/src/test/java/org/sonarsource/slang/visitors/TreePrinterTest.java new file mode 100644 index 00000000..17b9acf5 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/visitors/TreePrinterTest.java @@ -0,0 +1,91 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.visitors; + +import java.util.Arrays; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.impl.AssignmentExpressionTreeImpl; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.FunctionDeclarationTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.ModifierTreeImpl; +import org.sonarsource.slang.impl.TextRangeImpl; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.sonarsource.slang.utils.TreeCreationUtils.assignment; +import static org.sonarsource.slang.utils.TreeCreationUtils.binary; +import static org.sonarsource.slang.utils.TreeCreationUtils.identifier; +import static org.sonarsource.slang.utils.TreeCreationUtils.integerLiteral; + +class TreePrinterTest { + + @Test + void test() { + Tree x1 = new IdentifierTreeImpl(null, "x1"); + Tree var1 = new IdentifierTreeImpl(null, "var1"); + Tree literal1 = new LiteralTreeImpl(null, "42"); + Tree binaryExp = new BinaryExpressionTreeImpl(null, BinaryExpressionTree.Operator.PLUS, null, var1, literal1); + Tree assignExp = new AssignmentExpressionTreeImpl(null, AssignmentExpressionTree.Operator.EQUAL, x1, binaryExp); + Tree modifier = new ModifierTreeImpl(null, ModifierTree.Kind.PRIVATE); + Tree function = new FunctionDeclarationTreeImpl(null, singletonList(modifier), false, null, null, emptyList(), null, emptyList()); + Assertions.assertThat(TreePrinter.tree2string(Arrays.asList(assignExp, function))).isEqualTo( + "AssignmentExpressionTreeImpl EQUAL\n" + + " IdentifierTreeImpl x1\n" + + " BinaryExpressionTreeImpl PLUS\n" + + " IdentifierTreeImpl var1\n" + + " LiteralTreeImpl 42\n" + + "\n" + + "FunctionDeclarationTreeImpl\n" + + " ModifierTreeImpl PRIVATE\n"); + } + + @Test + void table_test() { + // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx = x-1; + Tree add = binary(BinaryExpressionTree.Operator.PLUS, + identifier("x", new TextRangeImpl(1,42,1,43),"x"), + integerLiteral("1", new TextRangeImpl(1,44,1,45), "1"), + new TextRangeImpl(1,42,1,45), "x", "1"); + + Tree assign = assignment(identifier("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", + new TextRangeImpl(1,8,1,39), "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), + add, + new TextRangeImpl(1,8,1,45),"x", "=", "x", "1"); + + String actual = TreePrinter.table(assign); + TreePrinter.Table expected = new TreePrinter.Table("AST node class", "first…last tokens", "line:col"); + expected.add("AssignmentExpressionTree {","x … 1","1:9 … 1:46"); + expected.add(" IdentifierTree","xxxxxxxxxxx…xxxxxxxxxxx","1:9 … 1:40"); + expected.add(" BinaryExpressionTree {","x … 1","1:43 … 1:46"); + expected.add(" IdentifierTree","x","1:43 … 1:44"); + expected.add(" IntegerLiteralTree","1","1:45 … 1:46"); + expected.add(" }","",""); + expected.add("}","",""); + assertEquals(expected.toString(), actual); + } +} diff --git a/slang-api/src/test/java/org/sonarsource/slang/visitors/TreeVisitorTest.java b/slang-api/src/test/java/org/sonarsource/slang/visitors/TreeVisitorTest.java new file mode 100644 index 00000000..20aaf9b1 --- /dev/null +++ b/slang-api/src/test/java/org/sonarsource/slang/visitors/TreeVisitorTest.java @@ -0,0 +1,82 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.visitors; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree.Operator; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class TreeVisitorTest { + + private class DummyNativeKind implements NativeKind {} + + private IdentifierTree var1 = new IdentifierTreeImpl(null, "var1"); + private LiteralTree number1 = new LiteralTreeImpl(null, "1"); + private BinaryExpressionTree binary = new BinaryExpressionTreeImpl(null, Operator.PLUS, null, var1, number1); + private BinaryExpressionTree binminus = new BinaryExpressionTreeImpl(null, Operator.MINUS, null, var1, var1); + + private DummyNativeKind nkind = new DummyNativeKind(); + private NativeTree nativeNode = new NativeTreeImpl(null, nkind, Arrays.asList(binary, binminus)); + + private TreeVisitor visitor = new TreeVisitor<>(); + + @Test + void visitSimpleTree() { + List visited = new ArrayList<>(); + visitor.register(Tree.class, (ctx, tree) -> visited.add(tree)); + visitor.scan(new TreeContext(), binary); + assertThat(visited).containsExactly(binary, var1, number1); + } + + @Test + void visitNativeTree() { + List visited = new ArrayList<>(); + visitor.register(Tree.class, (ctx, tree) -> visited.add(tree)); + visitor.scan(new TreeContext(), nativeNode); + assertThat(visited).containsExactly(nativeNode, binary, var1, number1, binminus, var1, var1); + } + + @Test + void ancestors() { + Map> ancestors = new HashMap<>(); + visitor.register(Tree.class, (ctx, tree) -> ancestors.put(tree, new ArrayList(ctx.ancestors()))); + visitor.scan(new TreeContext(), binary); + assertThat(ancestors.get(binary)).isEmpty(); + assertThat(ancestors.get(var1)).containsExactly(binary); + assertThat(ancestors.get(number1)).containsExactly(binary); + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/assignment_expression.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/assignment_expression.json new file mode 100644 index 00000000..a98b1d0f --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/assignment_expression.json @@ -0,0 +1,17 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::1", "text": "x"}, + {"textRange": "1:2::3", "text": "="}, + {"textRange": "1:4::5", "text": "2"} + ] + }, + "tree": { + "@type": "AssignmentExpression", + "metaData": "1:0::5", + "operator": "EQUAL", + "leftHandSide": {"@type": "Identifier", "metaData": "1:0::1", "name": "x"}, + "statementOrExpression": {"@type": "IntegerLiteral", "metaData": "1:4::5", "value": "2"} + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/binary_expression.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/binary_expression.json new file mode 100644 index 00000000..6cb97619 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/binary_expression.json @@ -0,0 +1,18 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::1", "text": "x"}, + {"textRange": "1:2::3", "text": "<"}, + {"textRange": "1:4::5", "text": "y"} + ] + }, + "tree": { + "@type": "BinaryExpression", + "metaData": "1:0::5", + "operator": "LESS_THAN", + "operatorToken": "1:2::3", + "leftOperand": {"@type": "Identifier", "metaData": "1:0::1", "name": "x"}, + "rightOperand": {"@type": "Identifier", "metaData": "1:4::5", "name": "y"} + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/block.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/block.json new file mode 100644 index 00000000..98d3d816 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/block.json @@ -0,0 +1,17 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::1", "text": "{"}, + {"textRange": "1:2::3", "text": "x"}, + {"textRange": "1:4::5", "text": "}"} + ] + }, + "tree": { + "@type": "Block", + "metaData": "1:0::5", + "statementOrExpressions": [ + {"@type": "Identifier", "metaData": "1:2::3", "name": "x"} + ] + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/catch_tree.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/catch_tree.json new file mode 100644 index 00000000..cbf22c40 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/catch_tree.json @@ -0,0 +1,17 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "catch", "type": "KEYWORD"}, + {"textRange": "1:8::9", "text": "x"}, + {"textRange": "1:10::11", "text": "y"} + ] + }, + "tree": { + "@type": "Catch", + "metaData": "1:0::11", + "catchParameter": {"@type": "Identifier", "metaData": "1:8::9", "name": "x"}, + "catchBlock": {"@type": "Identifier", "metaData": "1:10::11", "name": "y"}, + "keyword": "1:0::5" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/catch_tree_without_parameter.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/catch_tree_without_parameter.json new file mode 100644 index 00000000..f79b45c3 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/catch_tree_without_parameter.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "catch", "type": "KEYWORD"}, + {"textRange": "1:10::11", "text": "y"} + ] + }, + "tree": { + "@type": "Catch", + "metaData": "1:0::11", + "catchParameter": null, + "catchBlock": {"@type": "Identifier", "metaData": "1:10::11", "name": "y"}, + "keyword": "1:0::5" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/class_declaration.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/class_declaration.json new file mode 100644 index 00000000..3ab532ca --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/class_declaration.json @@ -0,0 +1,24 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "class", "type": "KEYWORD"}, + {"textRange": "1:6::7", "text": "A"}, + {"textRange": "1:8::9", "text": "{"}, + {"textRange": "1:10::11", "text": "}"} + ] + }, + "tree": { + "@type": "ClassDeclaration", + "metaData": "1:0::11", + "identifier": "1:6::7", + "classTree": { + "@type": "Native", + "metaData": "1:6::11", + "nativeKind": "CLASS", + "children": [ + {"@type": "Identifier", "metaData": "1:6::7", "name": "A"} + ] + } + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/class_declaration_anonymous.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/class_declaration_anonymous.json new file mode 100644 index 00000000..9263f350 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/class_declaration_anonymous.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "class", "type": "KEYWORD"}, + {"textRange": "1:8::9", "text": "{"}, + {"textRange": "1:10::11", "text": "}"} + ] + }, + "tree": { + "@type": "ClassDeclaration", + "metaData": "1:0::11", + "identifier": null, + "classTree": {"@type": "Native", "metaData": "1:8::11", "nativeKind": "CLASS", "children": []} + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/comment.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/comment.json new file mode 100644 index 00000000..adcedb2f --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/comment.json @@ -0,0 +1,6 @@ +{ + "text": "// hello", + "contentText": " hello", + "range": "3:7::15", + "contentRange": "3:9::15" +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_invalid_json_tree.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_invalid_json_tree.json new file mode 100644 index 00000000..ee29a8ce --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_invalid_json_tree.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:6", "text": "return", "type": "KEYWORD"}, + {"textRange": "1:7:1:11", "text": "1234", "type": "OTHER"}, + {"textRange": "1:11:1:12", "text": ";", "type": "OTHER"} + ] + }, + "tree": { + "@type": "Return", + "metaData": "1:0:1:12", + "body": 1234, + "keyword": "1:0:1:6" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_invalid_tree_type.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_invalid_tree_type.json new file mode 100644 index 00000000..e39d1597 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_invalid_tree_type.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:6", "text": "return", "type": "KEYWORD"}, + {"textRange": "1:7:1:11", "text": "true", "type": "OTHER"}, + {"textRange": "1:11:1:12", "text": ";", "type": "OTHER"} + ] + }, + "tree": { + "@type": "Return", + "metaData": "1:0:1:12", + "body": {"@type": "UnsupportedType", "metaData": "1:7:1:11"}, + "keyword": "1:0:1:6" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_missing_type.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_missing_type.json new file mode 100644 index 00000000..e7dace4e --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_missing_type.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:6", "text": "return", "type": "KEYWORD"}, + {"textRange": "1:7:1:11", "text": "true", "type": "OTHER"}, + {"textRange": "1:11:1:12", "text": ";", "type": "OTHER"} + ] + }, + "tree": { + "@type": "Return", + "metaData": "1:0:1:12", + "body": {"invalid_type": "Literal", "metaData": "1:7:1:11", "value": "true"}, + "keyword": "1:0:1:6" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unary_expression_with_null_child.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unary_expression_with_null_child.json new file mode 100644 index 00000000..5301141f --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unary_expression_with_null_child.json @@ -0,0 +1,15 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:1", "text": "-", "type": "OTHER"}, + {"textRange": "1:1:1:2", "text": "x", "type": "OTHER"} + ] + }, + "tree": { + "@type": "UnaryExpression", + "metaData": "1:0:1:2", + "operator": "MINUS", + "operand": null + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unary_expression_without_child.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unary_expression_without_child.json new file mode 100644 index 00000000..5bedfcfc --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unary_expression_without_child.json @@ -0,0 +1,14 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:1", "text": "-", "type": "OTHER"}, + {"textRange": "1:1:1:2", "text": "x", "type": "OTHER"} + ] + }, + "tree": { + "@type": "UnaryExpression", + "metaData": "1:0:1:2", + "operator": "MINUS" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unexpected_match_child_class.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unexpected_match_child_class.json new file mode 100644 index 00000000..f3282e02 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/error_unexpected_match_child_class.json @@ -0,0 +1,19 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:6", "text": "switch", "type": "KEYWORD"}, + {"textRange": "1:9:1:16", "text": "default", "type": "KEYWORD"}, + {"textRange": "1:17:1:19", "text": "42", "type": "OTHER"} + ] + }, + "tree": { + "@type": "Match", + "metaData": "1:0:1:19", + "expression": null, + "cases": [ + {"@type": "IntegerLiteral", "metaData": "1:17:1:19", "value": "42"} + ], + "keyword": "1:0:1:6" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/exception_handling.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/exception_handling.json new file mode 100644 index 00000000..609760f8 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/exception_handling.json @@ -0,0 +1,28 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::3", "text": "try", "type": "KEYWORD"}, + {"textRange": "1:10::11", "text": "x"}, + {"textRange": "1:20::25", "text": "catch", "type": "KEYWORD"}, + {"textRange": "1:30::31", "text": "y"}, + {"textRange": "1:40::47", "text": "finally", "type": "KEYWORD"}, + {"textRange": "1:50::51", "text": "z"} + ] + }, + "tree": { + "@type": "ExceptionHandling", + "metaData": "1:0::51", + "tryBlock": {"@type": "Identifier", "metaData": "1:10::11", "name": "x"}, + "tryKeyword": "1:0::3", + "catchBlocks": [ + {"@type": "Catch", + "metaData": "1:20::31", + "catchParameter": null, + "catchBlock": {"@type": "Identifier", "metaData": "1:30::31", "name": "y"}, + "keyword": "1:20::25" + } + ], + "finallyBlock": {"@type": "Identifier", "metaData": "1:50::51", "name": "z"} + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/exception_handling_without_catch.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/exception_handling_without_catch.json new file mode 100644 index 00000000..cd3faa9c --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/exception_handling_without_catch.json @@ -0,0 +1,17 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::3", "text": "try", "type": "KEYWORD"}, + {"textRange": "1:10::11", "text": "x"} + ] + }, + "tree": { + "@type": "ExceptionHandling", + "metaData": "1:0::11", + "tryBlock": {"@type": "Identifier", "metaData": "1:10::11", "name": "x"}, + "tryKeyword": "1:0::3", + "catchBlocks": [], + "finallyBlock": null + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/function_declaration.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/function_declaration.json new file mode 100644 index 00000000..d4c81755 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/function_declaration.json @@ -0,0 +1,31 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::6", "text": "public", "type": "KEYWORD"}, + {"textRange": "1:7::10", "text": "int"}, + {"textRange": "1:11::14", "text": "foo"}, + {"textRange": "1:15::20", "text": "param"}, + {"textRange": "1:20::21", "text": "{"}, + {"textRange": "1:22::23", "text": "}"}, + {"textRange": "1:24::26", "text": "->", "type": "KEYWORD"} + ] + }, + "tree": { + "@type": "FunctionDeclaration", + "metaData": "1:0::26", + "modifiers": [ + {"@type": "Native", "metaData": "1:0::6", "nativeKind": "modifier", "children": []} + ], + "isConstructor": true, + "returnType": {"@type": "Identifier", "metaData": "1:7::10", "name": "int"}, + "name": {"@type": "Identifier", "metaData": "1:11::14", "name": "foo"}, + "formalParameters": [ + {"@type": "Identifier", "metaData": "1:15::20", "name": "param"} + ], + "body": {"@type": "Block", "metaData": "1:20::23", "statementOrExpressions": []}, + "nativeChildren": [ + {"@type": "Native", "metaData": "1:24::26", "nativeKind": "arrow", "children": []} + ] + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/identifier.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/identifier.json new file mode 100644 index 00000000..6fd401d6 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/identifier.json @@ -0,0 +1,9 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::3", "text": "foo"} + ] + }, + "tree": {"@type": "Identifier", "metaData": "1:0::3", "name": "foo"} +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/if_tree.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/if_tree.json new file mode 100644 index 00000000..e3545f9c --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/if_tree.json @@ -0,0 +1,21 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::2", "text": "if", "type": "KEYWORD"}, + {"textRange": "1:3::7", "text": "true"}, + {"textRange": "1:8::9", "text": "x"}, + {"textRange": "1:10::14", "text": "else"}, + {"textRange": "1:15::16", "text": "y"} + ] + }, + "tree": { + "@type": "If", + "metaData": "1:0::16", + "condition": {"@type": "Literal", "metaData": "1:3::7", "value": "true"}, + "thenBranch": {"@type": "Identifier", "metaData": "1:8::9", "name": "x"}, + "elseBranch": {"@type": "Identifier", "metaData": "1:15::16", "name": "y"}, + "ifKeyword": "1:0::2", + "elseKeyword": "1:10::14" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/if_tree_without_else.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/if_tree_without_else.json new file mode 100644 index 00000000..8c9f136f --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/if_tree_without_else.json @@ -0,0 +1,19 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::2", "text": "if", "type": "KEYWORD"}, + {"textRange": "1:3::7", "text": "true"}, + {"textRange": "1:8::9", "text": "x"} + ] + }, + "tree": { + "@type": "If", + "metaData": "1:0::9", + "condition": {"@type": "Literal", "metaData": "1:3::7", "value": "true"}, + "thenBranch": {"@type": "Identifier", "metaData": "1:8::9", "name": "x"}, + "elseBranch": null, + "ifKeyword": "1:0::2", + "elseKeyword": null + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/import_declaration.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/import_declaration.json new file mode 100644 index 00000000..5aa21c7f --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/import_declaration.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::6", "text": "import", "type": "KEYWORD"}, + {"textRange": "1:7::10", "text": "lib"} + ] + }, + "tree": { + "@type": "ImportDeclaration", + "metaData": "1:0::10", + "children": [ + {"@type": "Identifier", "metaData": "1:7::10", "name": "lib"} + ] + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/integer_literal.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/integer_literal.json new file mode 100644 index 00000000..77bcaa8b --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/integer_literal.json @@ -0,0 +1,9 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::4", "text": "0xFF"} + ] + }, + "tree": {"@type": "IntegerLiteral", "metaData": "1:0::4", "value": "0xFF"} +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/jump.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/jump.json new file mode 100644 index 00000000..2498dcff --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/jump.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "break", "type": "KEYWORD"}, + {"textRange": "1:6::10", "text": "hard"} + ] + }, + "tree": { + "@type": "Jump", + "metaData": "1:0::10", + "label": {"@type": "Identifier", "metaData": "1:6::10", "name": "hard"}, + "keyword": "1:0::5", + "kind": "BREAK" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/literal.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/literal.json new file mode 100644 index 00000000..7cd4ef85 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/literal.json @@ -0,0 +1,9 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::4", "text": "true"} + ] + }, + "tree": {"@type": "Literal", "metaData": "1:0::4", "value": "true"} +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/loop.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/loop.json new file mode 100644 index 00000000..9c0b328e --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/loop.json @@ -0,0 +1,18 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "while", "type": "KEYWORD"}, + {"textRange": "1:6::10", "text": "true"}, + {"textRange": "1:11::12", "text": "x", "type": "KEYWORD"} + ] + }, + "tree": { + "@type": "Loop", + "metaData": "1:0::12", + "condition": {"@type": "Literal", "metaData": "1:6::10", "value": "true"}, + "body": {"@type": "Identifier", "metaData": "1:11::12", "name": "x"}, + "kind": "WHILE", + "keyword": "1:0::5" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/match.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/match.json new file mode 100644 index 00000000..2bfdcff0 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/match.json @@ -0,0 +1,25 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::6", "text": "switch", "type": "KEYWORD"}, + {"textRange": "1:7::8", "text": "x"}, + {"textRange": "1:9::16", "text": "default", "type": "KEYWORD"}, + {"textRange": "1:17::19", "text": "42"} + ] + }, + "tree": { + "@type": "Match", + "metaData": "1:0::19", + "expression": {"@type": "Identifier", "metaData": "1:7::8", "name": "x"}, + "cases": [ + { + "@type": "MatchCase", + "metaData": "1:9::19", + "expression": null, + "body": {"@type": "IntegerLiteral", "metaData": "1:17::19", "value": "42"} + } + ], + "keyword": "1:0::6" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/match_case.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/match_case.json new file mode 100644 index 00000000..689c7049 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/match_case.json @@ -0,0 +1,17 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::4", "text": "case", "type": "KEYWORD"}, + {"textRange": "1:5::6", "text": "7"}, + {"textRange": "1:7::8", "text": ":"}, + {"textRange": "1:9::10", "text": "x"} + ] + }, + "tree": { + "@type": "MatchCase", + "metaData": "1:0::10", + "expression": {"@type": "IntegerLiteral", "metaData": "1:5::6", "value": "7"}, + "body": {"@type": "Identifier", "metaData": "1:9::10", "name": "x"} + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/modifier.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/modifier.json new file mode 100644 index 00000000..7069512d --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/modifier.json @@ -0,0 +1,9 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::6", "text": "public", "type": "KEYWORD"} + ] + }, + "tree": {"@type": "Modifier", "metaData": "1:0::6", "kind": "PUBLIC"} +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree.json new file mode 100644 index 00000000..ba39f12d --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree.json @@ -0,0 +1,19 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "10:0::7", "text": "token10"}, + {"textRange": "21:0::7", "text": "token21"}, + {"textRange": "22:0::7", "text": "token22"} + ] + }, + "tree": { + "@type": "Native", + "metaData": "10:0:22:7", + "nativeKind": "PARENT", + "children": [ + {"@type": "Native", "metaData": "21:0::7", "nativeKind": "CHILD", "children": []}, + {"@type": "Native", "metaData": "22:0::7", "nativeKind": "CHILD", "children": []} + ] + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree_empty_kind.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree_empty_kind.json new file mode 100644 index 00000000..95faead6 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree_empty_kind.json @@ -0,0 +1,24 @@ +{ + "treeMetaData": { + "comments": [ + + ], + "tokens": [ + { + "textRange": "1:0::1", + "text": "x" + } + ] + }, + "tree": { + "@type": "Native", + "metaData": "1:0::1", + "children": [ + { + "@type": "Identifier", + "metaData": "1:0::1", + "name": "MyClass" + } + ] + } +} \ No newline at end of file diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree_with_kind.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree_with_kind.json new file mode 100644 index 00000000..15f4315f --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/native_tree_with_kind.json @@ -0,0 +1,25 @@ +{ + "treeMetaData": { + "comments": [ + + ], + "tokens": [ + { + "textRange": "1:0::1", + "text": "x" + } + ] + }, + "tree": { + "@type": "Native", + "metaData": "1:0::1", + "nativeKind": "kind", + "children": [ + { + "@type": "Identifier", + "metaData": "1:0::1", + "name": "MyClass" + } + ] + } +} \ No newline at end of file diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/nullable_child_can_be_omitted.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/nullable_child_can_be_omitted.json new file mode 100644 index 00000000..885d25fa --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/nullable_child_can_be_omitted.json @@ -0,0 +1,14 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:5", "text": "break", "type": "KEYWORD"} + ] + }, + "tree": { + "@type": "Jump", + "metaData": "1:0:1:5", + "keyword": "1:0:1:5", + "kind": "BREAK" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/package_declaration.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/package_declaration.json new file mode 100644 index 00000000..ae1776b8 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/package_declaration.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::7", "text": "package", "type": "KEYWORD"}, + {"textRange": "1:8::13", "text": "hello"} + ] + }, + "tree": { + "@type": "PackageDeclaration", + "metaData": "1:0::13", + "children": [ + {"@type": "Identifier", "metaData": "1:8::13", "name": "hello"} + ] + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/parameter.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/parameter.json new file mode 100644 index 00000000..6942bb3b --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/parameter.json @@ -0,0 +1,21 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::9", "text": "@Nullable"}, + {"textRange": "2:0::1", "text": "x"}, + {"textRange": "2:2::5", "text": "int"}, + {"textRange": "2:6::8", "text": "42"} + ] + }, + "tree": { + "@type": "Parameter", + "metaData": "1:0:2:8", + "identifier": {"@type": "Identifier", "metaData": "2:0::1", "name": "x"}, + "type": {"@type": "Identifier", "metaData": "2:2::5", "name": "int"}, + "defaultValue": {"@type": "IntegerLiteral", "metaData": "2:6::8", "value": "42"}, + "modifiers": [ + {"@type": "Identifier", "metaData": "1:0::9", "name": "@Nullable"} + ] + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/parenthesized_expression.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/parenthesized_expression.json new file mode 100644 index 00000000..5383a88e --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/parenthesized_expression.json @@ -0,0 +1,17 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::1", "text": "("}, + {"textRange": "1:1::2", "text": "x"}, + {"textRange": "1:2::3", "text": ")"} + ] + }, + "tree": { + "@type": "ParenthesizedExpression", + "metaData": "1:0::3", + "expression": {"@type": "Identifier", "metaData": "1:1::2", "name": "x"}, + "leftParenthesis": "1:0::1", + "rightParenthesis": "1:2::3" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/place_holder.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/place_holder.json new file mode 100644 index 00000000..fccc5cb1 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/place_holder.json @@ -0,0 +1,9 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::1", "text": "_", "type": "KEYWORD"} + ] + }, + "tree": {"@type": "PlaceHolder", "metaData": "1:0::1", "placeHolderToken": "1:0::1"} +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/return_true.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/return_true.json new file mode 100644 index 00000000..4702327a --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/return_true.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::6", "text": "return", "type": "KEYWORD"}, + {"textRange": "1:7::11", "text": "true"}, + {"textRange": "1:11::12", "text": ";"} + ] + }, + "tree": { + "@type": "Return", + "metaData": "1:0::12", + "body": {"@type": "Literal", "metaData": "1:7::11", "value": "true"}, + "keyword": "1:0::6" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/string_literal.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/string_literal.json new file mode 100644 index 00000000..6f76b471 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/string_literal.json @@ -0,0 +1,9 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::3", "text": "\"a\"", "type": "STRING_LITERAL"} + ] + }, + "tree": {"@type": "StringLiteral", "metaData": "1:0::3", "content": "a", "value": "\"a\""} +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/throw_nothing.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/throw_nothing.json new file mode 100644 index 00000000..503184b5 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/throw_nothing.json @@ -0,0 +1,9 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "throw", "type": "KEYWORD"} + ] + }, + "tree": {"@type": "Throw", "metaData": "1:0::5", "keyword": "1:0::5", "body": null} +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/throw_tree.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/throw_tree.json new file mode 100644 index 00000000..4e09f946 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/throw_tree.json @@ -0,0 +1,15 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::5", "text": "throw", "type": "KEYWORD"}, + {"textRange": "1:6::8", "text": "ex"} + ] + }, + "tree": { + "@type": "Throw", + "metaData": "1:0::8", + "keyword": "1:0::5", + "body": {"@type": "Identifier", "metaData": "1:6::8", "name": "ex"} + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/token_keyword.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/token_keyword.json new file mode 100644 index 00000000..ddf5cc73 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/token_keyword.json @@ -0,0 +1,5 @@ +{ + "textRange": "1:2::5", + "text": "key", + "type": "KEYWORD" +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/token_other.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/token_other.json new file mode 100644 index 00000000..7e0e2b5d --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/token_other.json @@ -0,0 +1,4 @@ +{ + "textRange": "3:7::10", + "text": "foo" +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/top_level.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/top_level.json new file mode 100644 index 00000000..b3ab7186 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/top_level.json @@ -0,0 +1,18 @@ +{ + "treeMetaData": { + "comments": [ + {"text": "// hello", "contentText": " hello", "range": "1:0::8", "contentRange": "1:2::8"} + ], + "tokens": [ + {"textRange": "2:0::4", "text": "true"} + ] + }, + "tree": { + "@type": "TopLevel", + "metaData": "1:0:2:4", + "declarations": [ + {"@type": "Literal", "metaData": "2:0::4", "value": "true"} + ], + "firstCpdToken": "2:0::4" + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/tree_metadata_provider.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/tree_metadata_provider.json new file mode 100644 index 00000000..56e7164d --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/tree_metadata_provider.json @@ -0,0 +1,8 @@ +{ + "comments": [ + {"text": "// hello", "contentText": " hello", "range": "1:0::8", "contentRange": "1:2::8"} + ], + "tokens": [ + {"textRange": "2:0::3", "text": "fun", "type": "KEYWORD"} + ] +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/unary_expression.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/unary_expression.json new file mode 100644 index 00000000..06d2888b --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/unary_expression.json @@ -0,0 +1,15 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::1", "text": "-"}, + {"textRange": "1:1::2", "text": "x"} + ] + }, + "tree": { + "@type": "UnaryExpression", + "metaData": "1:0::2", + "operator": "MINUS", + "operand": {"@type": "Identifier", "metaData": "1:1::2", "name": "x"} + } +} diff --git a/slang-api/src/test/resources/org/sonarsource/slang/persistence/variable_declaration.json b/slang-api/src/test/resources/org/sonarsource/slang/persistence/variable_declaration.json new file mode 100644 index 00000000..63193226 --- /dev/null +++ b/slang-api/src/test/resources/org/sonarsource/slang/persistence/variable_declaration.json @@ -0,0 +1,18 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0::3", "text": "int"}, + {"textRange": "1:4::5", "text": "x"}, + {"textRange": "1:6::8", "text": "42"} + ] + }, + "tree": { + "@type": "VariableDeclaration", + "metaData": "1:0::8", + "identifier": {"@type": "Identifier", "metaData": "1:4::5", "name": "x"}, + "type": {"@type": "Identifier", "metaData": "1:0::3", "name": "int"}, + "initializer": {"@type": "IntegerLiteral", "metaData": "1:6::8", "value": "42"}, + "isVal": true + } +} diff --git a/slang-checks/build.gradle b/slang-checks/build.gradle new file mode 100644 index 00000000..9fa4c93b --- /dev/null +++ b/slang-checks/build.gradle @@ -0,0 +1,13 @@ +dependencies { + implementation project(':slang-api') + compileOnly 'org.sonarsource.api.plugin:sonar-plugin-api' + implementation 'com.google.code.findbugs:jsr305' + implementation 'org.sonarsource.analyzer-commons:sonar-analyzer-commons' + testImplementation project(':slang-antlr') + testImplementation project(':slang-testing') + testImplementation "org.junit.jupiter:junit-jupiter-api" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.sonarsource.analyzer-commons:sonar-analyzer-test-commons' + testImplementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures' +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/AbstractBranchDuplicationCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/AbstractBranchDuplicationCheck.java new file mode 100644 index 00000000..2c1b71c0 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/AbstractBranchDuplicationCheck.java @@ -0,0 +1,100 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.ArrayList; +import java.util.List; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; + +public abstract class AbstractBranchDuplicationCheck implements SlangCheck { + + protected abstract void checkDuplicatedBranches(CheckContext ctx, Tree tree, List branches); + + protected abstract void onAllIdenticalBranches(CheckContext ctx, Tree tree); + + @Override + public void initialize(InitContext init) { + init.register(IfTree.class, (ctx, tree) -> { + Tree parent = ctx.parent(); + if (!(parent instanceof IfTree) || tree == ((IfTree) parent).thenBranch()) { + checkConditionalStructure(ctx, tree, new ConditionalStructure(tree)); + } + }); + init.register(MatchTree.class, (ctx, tree) -> + checkConditionalStructure(ctx, tree, new ConditionalStructure(tree)) + ); + } + + protected void checkConditionalStructure(CheckContext ctx, Tree tree, ConditionalStructure conditional) { + if (conditional.allBranchesArePresent && conditional.allBranchesAreIdentical()) { + onAllIdenticalBranches(ctx, tree); + } else { + checkDuplicatedBranches(ctx, tree, conditional.branches); + } + } + + public static class ConditionalStructure { + + private boolean allBranchesArePresent = false; + + private final List branches = new ArrayList<>(); + + private ConditionalStructure(IfTree ifTree) { + branches.add(ifTree.thenBranch()); + Tree elseBranch = ifTree.elseBranch(); + while (elseBranch != null) { + if (elseBranch instanceof IfTree) { + IfTree elseIf = (IfTree) elseBranch; + branches.add(elseIf.thenBranch()); + elseBranch = elseIf.elseBranch(); + } else { + branches.add(elseBranch); + allBranchesArePresent = true; + elseBranch = null; + } + } + } + + private ConditionalStructure(MatchTree tree) { + for (MatchCaseTree caseTree : tree.cases()) { + branches.add(caseTree.body()); + if (caseTree.expression() == null) { + allBranchesArePresent = true; + } + } + } + + private boolean allBranchesAreIdentical() { + return branches.size() > 1 && + branches.stream() + .skip(1) + .allMatch(branch -> areEquivalent(branches.get(0), branch)); + } + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/AllBranchesIdenticalCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/AllBranchesIdenticalCheck.java new file mode 100644 index 00000000..ee989d2f --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/AllBranchesIdenticalCheck.java @@ -0,0 +1,40 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import java.util.List; +import org.sonar.check.Rule; + +@Rule(key = "S3923") +public class AllBranchesIdenticalCheck extends AbstractBranchDuplicationCheck { + + @Override + protected void checkDuplicatedBranches(CheckContext ctx, Tree tree, List branches) { + // handled by S1871 + } + + @Override + protected void onAllIdenticalBranches(CheckContext ctx, Tree tree) { + ctx.reportIssue(tree, "Remove this conditional structure or edit its code blocks so that they're not all the same."); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/BadClassNameCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/BadClassNameCheck.java new file mode 100644 index 00000000..f043be6b --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/BadClassNameCheck.java @@ -0,0 +1,55 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.regex.Pattern; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +@Rule(key = "S101") +public class BadClassNameCheck implements SlangCheck { + + private static final String DEFAULT_FORMAT = "^[A-Z][a-zA-Z0-9]*$"; + + @RuleProperty( + key = "format", + description = "Regular expression used to check the class names against.", + defaultValue = DEFAULT_FORMAT) + public String format = DEFAULT_FORMAT; + + @Override + public void initialize(InitContext init) { + Pattern pattern = Pattern.compile(format); + init.register(ClassDeclarationTree.class, (ctx, tree) -> { + IdentifierTree identifier = tree.identifier(); + if (identifier != null && !pattern.matcher(identifier.name()).matches()) { + String message = String.format( + "Rename class \"%s\" to match the regular expression %s.", + identifier.name(), format); + ctx.reportIssue(identifier, message); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/BadFunctionNameCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/BadFunctionNameCheck.java new file mode 100644 index 00000000..14035c19 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/BadFunctionNameCheck.java @@ -0,0 +1,60 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.regex.Pattern; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; + +@Rule(key = "S100") +public class BadFunctionNameCheck implements SlangCheck { + + public static final String DEFAULT_FORMAT = "^[a-z][a-zA-Z0-9]*$"; + + @RuleProperty( + key = "format", + description = "Regular expression used to check the function names against." + ) + @PropertyDefaultValue(language = Language.RUBY, defaultValue = Language.RUBY_NAMING_DEFAULT) + @PropertyDefaultValue(language = Language.SCALA, defaultValue = Language.SCALA_FUNCTION_OR_OPERATOR_NAMING_DEFAULT) + @PropertyDefaultValue(language = Language.GO, defaultValue = Language.GO_NAMING_DEFAULT) + public String format = DEFAULT_FORMAT; + + private String message(String name) { + return "Rename function \"" + name + "\" to match the regular expression " + format; + } + + @Override + public void initialize(InitContext init) { + Pattern pattern = Pattern.compile(format); + init.register(FunctionDeclarationTree.class, (ctx, fnDeclarationTree) -> { + IdentifierTree name = fnDeclarationTree.name(); + if (!fnDeclarationTree.isConstructor() && name != null && !pattern.matcher(name.name()).matches()) { + ctx.reportIssue(fnDeclarationTree.name(), message(name.name())); + } + }); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/BooleanInversionCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/BooleanInversionCheck.java new file mode 100644 index 00000000..1375bfa8 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/BooleanInversionCheck.java @@ -0,0 +1,65 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree.Operator; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.EnumMap; +import java.util.Map; +import org.sonar.check.Rule; + +import static org.sonarsource.slang.checks.utils.ExpressionUtils.skipParentheses; + +@Rule(key = "S1940") +public class BooleanInversionCheck implements SlangCheck { + + private static final Map OPERATORS = createOperatorsMap(); + + private static Map createOperatorsMap() { + Map operatorsMap = new EnumMap<>(Operator.class); + operatorsMap.put(Operator.EQUAL_TO, "!="); + operatorsMap.put(Operator.NOT_EQUAL_TO, "=="); + operatorsMap.put(Operator.LESS_THAN, ">="); + operatorsMap.put(Operator.GREATER_THAN, "<="); + operatorsMap.put(Operator.LESS_THAN_OR_EQUAL_TO, ">"); + operatorsMap.put(Operator.GREATER_THAN_OR_EQUAL_TO, "<"); + return operatorsMap; + } + + @Override + public void initialize(InitContext init) { + init.register(UnaryExpressionTree.class, (ctx, tree) -> { + Tree innerExpression = skipParentheses(tree.operand()); + if (tree.operator() == UnaryExpressionTree.Operator.NEGATE && innerExpression instanceof BinaryExpressionTree) { + BinaryExpressionTree binaryExpression = (BinaryExpressionTree) innerExpression; + String oppositeOperator = OPERATORS.get(binaryExpression.operator()); + if (oppositeOperator != null) { + String message = String.format("Use the opposite operator (\"%s\") instead.", oppositeOperator); + ctx.reportIssue(tree, message); + } + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/BooleanLiteralCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/BooleanLiteralCheck.java new file mode 100644 index 00000000..c642804d --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/BooleanLiteralCheck.java @@ -0,0 +1,84 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import javax.annotation.Nullable; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.ExpressionUtils; + +@Rule(key = "S1125") +public class BooleanLiteralCheck implements SlangCheck { + private static final List CONDITIONAL_BINARY_OPERATORS = Arrays.asList( + BinaryExpressionTree.Operator.CONDITIONAL_AND, + BinaryExpressionTree.Operator.CONDITIONAL_OR); + + private static final String MESSAGE = "Remove the unnecessary Boolean literal."; + + @Override + public void initialize(InitContext init) { + init.register(IfTree.class, (ctx, ifTree) -> { + if (isIfWithMaxTwoBranches(ctx.parent(), ifTree) && !hasBlockBranch(ifTree)) { + getBooleanLiteral(ifTree.thenBranch(), ifTree.elseBranch()) + .ifPresent(booleanLiteral -> ctx.reportIssue(booleanLiteral, MESSAGE)); + } + }); + + init.register(BinaryExpressionTree.class, (ctx, binaryExprTree) -> { + if (CONDITIONAL_BINARY_OPERATORS.contains(binaryExprTree.operator())) { + getBooleanLiteral(binaryExprTree.leftOperand(), binaryExprTree.rightOperand()) + .ifPresent(booleanLiteral -> ctx.reportIssue(booleanLiteral, MESSAGE)); + } + }); + + init.register(UnaryExpressionTree.class, (ctx, unaryExprTree) -> { + if (UnaryExpressionTree.Operator.NEGATE.equals(unaryExprTree.operator())) { + getBooleanLiteral(unaryExprTree.operand()) + .ifPresent(booleanLiteral -> ctx.reportIssue(booleanLiteral, MESSAGE)); + } + }); + } + + private static boolean isIfWithMaxTwoBranches(@Nullable Tree parent, IfTree ifTree) { + boolean isElseIf = parent instanceof IfTree && ((IfTree) parent).elseBranch() == ifTree; + boolean isIfElseIf = ifTree.elseBranch() instanceof IfTree; + return !isElseIf && !isIfElseIf; + } + + private static boolean hasBlockBranch(IfTree ifTree) { + return ifTree.thenBranch() instanceof BlockTree || ifTree.elseBranch() instanceof BlockTree; + } + + private static Optional getBooleanLiteral(Tree... trees) { + return Arrays.stream(trees) + .map(ExpressionUtils::skipParentheses) + .filter(ExpressionUtils::isBooleanLiteral) + .findFirst(); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/CheckList.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/CheckList.java new file mode 100644 index 00000000..848bb93f --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/CheckList.java @@ -0,0 +1,92 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CheckList { + + // these checks should be explicitly added to 'checks' in Sensor constructor + // and language RulesDefinition + // this list should be maintained only for documentation purposes (keep the track of all existing checks in this class) and for testing + static final Class[] ALL_CHECKS_WITH_LANGUAGE_CONFIG = { + CommentedCodeCheck.class, + }; + + private CheckList() { + } + + static List> allChecks() { + return Arrays.asList( + AllBranchesIdenticalCheck.class, + BadClassNameCheck.class, + BadFunctionNameCheck.class, + BooleanInversionCheck.class, + BooleanLiteralCheck.class, + CodeAfterJumpCheck.class, + CollapsibleIfStatementsCheck.class, + DuplicateBranchCheck.class, + DuplicatedFunctionImplementationCheck.class, + ElseIfWithoutElseCheck.class, + EmptyBlockCheck.class, + EmptyCommentCheck.class, + EmptyFunctionCheck.class, + FileHeaderCheck.class, + FixMeCommentCheck.class, + FunctionCognitiveComplexityCheck.class, + HardcodedCredentialsCheck.class, + HardcodedIpCheck.class, + IdenticalBinaryOperandCheck.class, + IdenticalConditionsCheck.class, + IfConditionalAlwaysTrueOrFalseCheck.class, + MatchCaseTooBigCheck.class, + MatchWithoutElseCheck.class, + NestedMatchCheck.class, + OctalValuesCheck.class, + OneStatementPerLineCheck.class, + ParsingErrorCheck.class, + RedundantParenthesesCheck.class, + SelfAssignmentCheck.class, + StringLiteralDuplicatedCheck.class, + TabsCheck.class, + TodoCommentCheck.class, + TooComplexExpressionCheck.class, + TooDeeplyNestedStatementsCheck.class, + TooLongFunctionCheck.class, + TooLongLineCheck.class, + TooManyLinesOfCodeFileCheck.class, + TooManyCasesCheck.class, + TooManyParametersCheck.class, + UnusedFunctionParameterCheck.class, + UnusedLocalVariableCheck.class, + UnusedPrivateMethodCheck.class, + VariableAndParameterNameCheck.class, + WrongAssignmentOperatorCheck.class); + } + + public static List> excludeChecks(Class[] blackList) { + List> checks = new ArrayList<>(allChecks()); + checks.removeAll(Arrays.asList(blackList)); + return checks; + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/CodeAfterJumpCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/CodeAfterJumpCheck.java new file mode 100644 index 00000000..bb14bd0e --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/CodeAfterJumpCheck.java @@ -0,0 +1,80 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.List; + +import org.sonar.check.Rule; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.HasKeyword; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.ReturnTree; +import org.sonarsource.slang.api.ThrowTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + + +@Rule(key = "S1763") +public class CodeAfterJumpCheck implements SlangCheck { + private static final String MESSAGE = "Refactor this piece of code to not have any dead code after this \"%s\"."; + + @Override + public void initialize(InitContext init) { + init.register(BlockTree.class, (ctx, blockTree) -> checkStatements(ctx, blockTree.statementOrExpressions())); + } + + protected boolean isValidAfterJump(Tree tree) { + return false; + } + + protected boolean shouldIgnore(Tree tree) { + return false; + } + + private void checkStatements(CheckContext ctx, List statementsOrExpressions) { + if (statementsOrExpressions.size() < 2) { + return; + } + + int index = 0; + while (index < statementsOrExpressions.size() - 1){ + Tree current = statementsOrExpressions.get(index); + index++; + + Tree next = statementsOrExpressions.get(index); + while (index < statementsOrExpressions.size() && shouldIgnore(next)){ + next = statementsOrExpressions.get(index); + index++; + } + + if (isJump(current) && + !shouldIgnore(next) && + !isValidAfterJump(next)) { + ctx.reportIssue(current, String.format(MESSAGE, ((HasKeyword) current).keyword().text())); + } + } + } + + private static boolean isJump(Tree tree){ + return tree instanceof JumpTree || tree instanceof ReturnTree || tree instanceof ThrowTree; + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/CollapsibleIfStatementsCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/CollapsibleIfStatementsCheck.java new file mode 100644 index 00000000..6f15d87e --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/CollapsibleIfStatementsCheck.java @@ -0,0 +1,67 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.Optional; +import org.sonar.check.Rule; + +@Rule(key = "S1066") +public class CollapsibleIfStatementsCheck implements SlangCheck { + private static final String MESSAGE_TEMPLATE = "Merge this \"%s\" statement with the nested one."; + private static final String SECONDARY_MESSAGE_TEMPLATE = "Nested \"%s\" statement"; + + @Override + public void initialize(InitContext init) { + init.register(IfTree.class, (ctx, ifTreeStatement) -> { + if (ifTreeStatement.elseBranch() == null) { + getCollapsibleIfStatement(ifTreeStatement.thenBranch()) + .ifPresent(innerIfStatement -> { + TextRange innerIfRange = innerIfStatement.ifKeyword().textRange(); + String message = String.format(MESSAGE_TEMPLATE, ifTreeStatement.ifKeyword().text()); + String secondaryMessage = String.format(SECONDARY_MESSAGE_TEMPLATE, innerIfStatement.ifKeyword().text()); + ctx.reportIssue(ifTreeStatement.ifKeyword(), message, new SecondaryLocation(innerIfRange, secondaryMessage)); + }); + } + }); + } + + private static Optional getCollapsibleIfStatement(Tree tree) { + if (tree instanceof BlockTree) { + BlockTree blockTree = (BlockTree) tree; + return blockTree.statementOrExpressions().size() == 1 + ? getIfStatementWithoutElse(tree.children().get(0)) + : Optional.empty(); + } + return getIfStatementWithoutElse(tree); + } + + private static Optional getIfStatementWithoutElse(Tree tree) { + return tree instanceof IfTree && ((IfTree) tree).elseBranch() == null + ? Optional.of((IfTree) tree) + : Optional.empty(); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/CommentedCodeCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/CommentedCodeCheck.java new file mode 100644 index 00000000..e1149568 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/CommentedCodeCheck.java @@ -0,0 +1,93 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.CodeVerifier; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.impl.TextRanges; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.check.Rule; + +@Rule(key = "S125") +public class CommentedCodeCheck implements SlangCheck { + private CodeVerifier codeVerifier; + + public CommentedCodeCheck(CodeVerifier codeVerifier) { + this.codeVerifier = codeVerifier; + } + + private static final String MESSAGE = "Remove this commented out code."; + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> { + List> groupedComments = + groupComments(tree.allComments()); + groupedComments.forEach(comments -> { + String content = comments.stream() + .map(Comment::contentText) + .collect(Collectors.joining("\n")); + if (codeVerifier.containsCode(content)) { + List textRanges = comments.stream() + .map(HasTextRange::textRange) + .collect(Collectors.toList()); + ctx.reportIssue(TextRanges.merge(textRanges), MESSAGE); + } + }); + }); + } + + private static List> groupComments(List comments) { + List> groups = new ArrayList<>(); + List currentGroup = null; + for (Comment comment : comments) { + if (currentGroup == null) { + currentGroup = initNewGroup(comment); + } else if (areAdjacent(currentGroup.get(currentGroup.size() - 1), comment)) { + currentGroup.add(comment); + } else { + groups.add(currentGroup); + currentGroup = initNewGroup(comment); + } + } + if (currentGroup != null) { + groups.add(currentGroup); + } + return groups; + } + + private static List initNewGroup(Comment comment) { + List group = new ArrayList<>(); + group.add(comment); + return group; + } + + private static boolean areAdjacent(Comment commentA, Comment commentB) { + return commentA.textRange().start().line() + 1 == commentB.textRange().start().line(); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/DuplicateBranchCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/DuplicateBranchCheck.java new file mode 100644 index 00000000..4876a228 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/DuplicateBranchCheck.java @@ -0,0 +1,75 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import javax.annotation.Nullable; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.utils.SyntacticEquivalence; +import java.util.List; +import org.sonar.check.Rule; + +@Rule(key = "S1871") +public class DuplicateBranchCheck extends AbstractBranchDuplicationCheck { + + @Override + protected void checkDuplicatedBranches(CheckContext ctx, Tree tree, List branches) { + for (List group : SyntacticEquivalence.findDuplicatedGroups(branches)) { + Tree original = group.get(0); + group.stream().skip(1) + .filter(DuplicateBranchCheck::spansMultipleLines) + .forEach(duplicated -> { + TextRange originalRange = original.metaData().textRange(); + ctx.reportIssue( + duplicated, + "This branch's code block is the same as the block for the branch on line " + originalRange.start().line() + ".", + new SecondaryLocation(originalRange, "Original")); + }); + } + + } + + @Override + protected void onAllIdenticalBranches(CheckContext ctx, Tree tree) { + // handled by S3923 + } + + protected static boolean spansMultipleLines(@Nullable Tree tree) { + if (tree == null) { + return false; + } + if (tree instanceof BlockTree) { + BlockTree block = (BlockTree) tree; + List statements = block.statementOrExpressions(); + if (statements.isEmpty()) { + return false; + } + Tree firstStatement = statements.get(0); + Tree lastStatement = statements.get(statements.size() - 1); + return firstStatement.metaData().textRange().start().line() != lastStatement.metaData().textRange().end().line(); + } + TextRange range = tree.metaData().textRange(); + return range.start().line() < range.end().line(); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/DuplicatedFunctionImplementationCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/DuplicatedFunctionImplementationCheck.java new file mode 100644 index 00000000..e05530cb --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/DuplicatedFunctionImplementationCheck.java @@ -0,0 +1,120 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.IntStream; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.visitors.TreeContext; +import org.sonarsource.slang.visitors.TreeVisitor; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; + +@Rule(key = "S4144") +public class DuplicatedFunctionImplementationCheck implements SlangCheck { + + private static final String MESSAGE = "Update this function so that its implementation is not identical to \"%s\" on line %s."; + private static final String MESSAGE_NO_NAME = "Update this function so that its implementation is not identical to the one on line %s."; + private static final int MINIMUM_STATEMENTS_COUNT = 2; + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> { + Map> functionsByParents = new HashMap<>(); + TreeVisitor functionVisitor = new TreeVisitor<>(); + functionVisitor.register(FunctionDeclarationTree.class, (functionCtx, functionDeclarationTree) -> { + if (!functionDeclarationTree.isConstructor()) { + functionsByParents + .computeIfAbsent(functionCtx.ancestors().peek(), key -> new ArrayList<>()) + .add(functionDeclarationTree); + } + }); + functionVisitor.scan(new TreeContext(), tree); + + for (Map.Entry> entry : functionsByParents.entrySet()) { + check(ctx, entry.getValue()); + } + }); + } + + private static void check(CheckContext ctx, List functionDeclarations) { + Set reportedDuplicates = new HashSet<>(); + IntStream.range(0, functionDeclarations.size()).forEach(i -> { + FunctionDeclarationTree original = functionDeclarations.get(i); + functionDeclarations.stream() + .skip(i + 1L) + .filter(f -> !reportedDuplicates.contains(f)) + .filter(DuplicatedFunctionImplementationCheck::hasMinimumSize) + .filter(f -> areDuplicatedImplementation(original, f)) + .forEach(duplicate -> { + reportDuplicate(ctx, original, duplicate); + reportedDuplicates.add(duplicate); + }); + }); + + } + + private static boolean hasMinimumSize(FunctionDeclarationTree function) { + BlockTree functionBody = function.body(); + if (functionBody == null) { + return false; + } + return functionBody.statementOrExpressions().size() >= MINIMUM_STATEMENTS_COUNT; + } + + private static boolean areDuplicatedImplementation(FunctionDeclarationTree original, FunctionDeclarationTree possibleDuplicate) { + return areEquivalent(original.nativeChildren(), possibleDuplicate.nativeChildren()) + && areEquivalent(original.formalParameters(), possibleDuplicate.formalParameters()) + && areEquivalent(original.body(), possibleDuplicate.body()); + } + + private static void reportDuplicate(CheckContext ctx, FunctionDeclarationTree original, FunctionDeclarationTree duplicate) { + IdentifierTree identifier = original.name(); + int line = original.metaData().textRange().start().line(); + String message; + Tree secondaryTree; + if (identifier != null) { + secondaryTree = identifier; + message = String.format(MESSAGE, identifier.name(), line); + } else { + secondaryTree = original; + message = String.format(MESSAGE_NO_NAME, line); + } + SecondaryLocation secondaryLocation = new SecondaryLocation(secondaryTree, "original implementation"); + IdentifierTree duplicateIdentifier = duplicate.name(); + Tree primaryTree = duplicateIdentifier != null ? duplicateIdentifier : duplicate; + ctx.reportIssue(primaryTree, message, secondaryLocation); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/ElseIfWithoutElseCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/ElseIfWithoutElseCheck.java new file mode 100644 index 00000000..659d4d7f --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/ElseIfWithoutElseCheck.java @@ -0,0 +1,100 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonar.check.Rule; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.JumpTree; +import org.sonarsource.slang.api.ReturnTree; +import org.sonarsource.slang.api.ThrowTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.impl.TextRangeImpl; + +import java.util.List; + +@Rule(key = "S126") +public class ElseIfWithoutElseCheck implements SlangCheck { + + private static final String MESSAGE = "Add the missing \"else\" clause."; + + @Override + public void initialize(InitContext init) { + init.register(IfTree.class, (ctx, ifTree) -> { + if (ifTree.elseBranch() == null || !isTopLevelIf(ctx, ifTree)) { + return; + } + + IfTree prevTree = ifTree; + boolean endsWithReturn = endsWithReturnBreakOrThrow(ifTree); + while (ifTree.elseBranch() instanceof IfTree) { + prevTree = ifTree; + ifTree = (IfTree)(ifTree.elseBranch()); + endsWithReturn = endsWithReturn && endsWithReturnBreakOrThrow(ifTree); + } + + // We raise an issue if + // - at least one branch does not finish with return/break/throw + // - no "else" is defined + if (!endsWithReturn && ifTree.elseBranch() == null) { + Token elseToken = prevTree.elseKeyword(); + Token ifToken = ifTree.ifKeyword(); + TextRange textRange = new TextRangeImpl( + elseToken.textRange().start(), + ifToken.textRange().end() + ); + ctx.reportIssue(textRange, MESSAGE); + } + + }); + } + + private static boolean isTopLevelIf(CheckContext ctx, IfTree ifTree) { + Tree firstAncestor = ctx.ancestors().getFirst(); + if (firstAncestor instanceof IfTree) { + // if ifTree is different from the else branch of firstAncestor, it means that ifTree is a statement inside + // firstAncestor and so ifTree is the top level "if" + return ((IfTree) firstAncestor).elseBranch() != ifTree; + } + return true; + } + + private static boolean endsWithReturnBreakOrThrow(IfTree ifTree) { + Tree thenBranch = ifTree.thenBranch(); + if (thenBranch instanceof BlockTree) { + List statements = ((BlockTree) thenBranch).statementOrExpressions(); + if (!statements.isEmpty()) { + Tree lastStmt = statements.get(statements.size() - 1); + return isReturnBreakOrThrow(lastStmt); + } + } + // Curly braces can be omitted when there is only one statement inside the "if" + return isReturnBreakOrThrow(thenBranch); + } + + private static boolean isReturnBreakOrThrow(Tree tree) { + return tree instanceof JumpTree || tree instanceof ReturnTree || tree instanceof ThrowTree; + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyBlockCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyBlockCheck.java new file mode 100644 index 00000000..fc70e775 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyBlockCheck.java @@ -0,0 +1,71 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import javax.annotation.Nullable; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + +@Rule(key = "S108") +public class EmptyBlockCheck implements SlangCheck { + + private static final String MESSAGE = "Either remove or fill this block of code."; + + @Override + public void initialize(InitContext init) { + init.register(BlockTree.class, (ctx, blockTree) -> { + Tree parent = ctx.parent(); + if (isValidBlock(parent) && blockTree.statementOrExpressions().isEmpty()) { + checkComments(ctx, blockTree); + } + }); + + init.register(MatchTree.class, (ctx, matchTree) -> { + if (matchTree.cases().isEmpty()) { + checkComments(ctx, matchTree); + } + }); + } + + private static boolean isValidBlock(@Nullable Tree parent) { + return !(parent instanceof FunctionDeclarationTree) + && !(parent instanceof NativeTree) + && !isWhileLoop(parent); + } + + private static boolean isWhileLoop(@Nullable Tree parent) { + return parent instanceof LoopTree && ((LoopTree) parent).kind() == LoopTree.LoopKind.WHILE; + } + + private static void checkComments(CheckContext ctx, Tree tree) { + if (tree.metaData().commentsInside().isEmpty()) { + ctx.reportIssue(tree, MESSAGE); + } + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyCommentCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyCommentCheck.java new file mode 100644 index 00000000..a599a5b6 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyCommentCheck.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonar.check.Rule; + +@Rule(key = "S4663") +public class EmptyCommentCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> + tree.allComments().stream() + .filter(comment -> comment.contentText().trim().isEmpty() && !comment.contentRange().end().equals(comment.textRange().end())) + .forEach(comment -> ctx.reportIssue(comment, "Remove this comment, it is empty."))); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyFunctionCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyFunctionCheck.java new file mode 100644 index 00000000..1b48874d --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/EmptyFunctionCheck.java @@ -0,0 +1,52 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonar.check.Rule; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + +@Rule(key = "S1186") +public class EmptyFunctionCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(FunctionDeclarationTree.class, (ctx, tree) -> { + BlockTree body = tree.body(); + if (!tree.isConstructor() && body != null && body.statementOrExpressions().isEmpty() && !hasComment(body, ctx.parent().metaData())) { + ctx.reportIssue(body, "Add a nested comment explaining why this function is empty or complete the implementation."); + } + }); + } + + private static boolean hasComment(BlockTree body, TreeMetaData parentMetaData) { + if (!body.metaData().commentsInside().isEmpty()) { + return true; + } + + int emptyBodyEndLine = body.textRange().end().line(); + return parentMetaData.commentsInside().stream() + .anyMatch(comment -> comment.contentRange().start().line() == emptyBodyEndLine); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/FileHeaderCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/FileHeaderCheck.java new file mode 100644 index 00000000..f69532e8 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/FileHeaderCheck.java @@ -0,0 +1,106 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +@Rule(key = "S1451") +public class FileHeaderCheck implements SlangCheck { + + private static final String MESSAGE = "Add or update the header of this file."; + private static final String DEFAULT_HEADER_FORMAT = ""; + + @RuleProperty( + key = "headerFormat", + description = "Expected copyright and license header", + defaultValue = DEFAULT_HEADER_FORMAT, + type = "TEXT") + public String headerFormat = DEFAULT_HEADER_FORMAT; + + @RuleProperty( + key = "isRegularExpression", + description = "Whether the headerFormat is a regular expression", + defaultValue = "false") + public boolean isRegularExpression = false; + private Pattern searchPattern = null; + private String[] expectedLines = null; + + private static final String LINES_REGEX = "\r\n|\n|\r"; + + @Override + public void initialize(InitContext init) { + initializeParameters(); + init.register(TopLevelTree.class, (ctx, tree) -> { + if (isRegularExpression) { + checkRegularExpression(ctx); + } else { + checkExpectedLines(ctx); + } + }); + } + + private void initializeParameters() { + if (isRegularExpression) { + try { + searchPattern = Pattern.compile(getHeaderFormat(), Pattern.DOTALL); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("[" + getClass().getSimpleName() + "] Unable to compile the regular expression: " + headerFormat, e); + } + } else { + expectedLines = headerFormat.split(LINES_REGEX); + } + } + + private void checkExpectedLines(CheckContext ctx) { + String[] lines = ctx.fileContent().split(LINES_REGEX, -1); + if (lines.length < expectedLines.length) { + ctx.reportFileIssue(MESSAGE); + } else { + IntStream.range(0, expectedLines.length) + .filter(lineIndex -> !lines[lineIndex].equals(expectedLines[lineIndex])) + .findFirst() + .ifPresent(lineIndex -> ctx.reportFileIssue(MESSAGE)); + } + } + + private void checkRegularExpression(CheckContext ctx) { + Matcher matcher = searchPattern.matcher(ctx.fileContent()); + if (!matcher.find()) { + ctx.reportFileIssue(MESSAGE); + } + } + + private String getHeaderFormat() { + String format = headerFormat; + if (format.charAt(0) != '^') { + format = "^" + format; + } + return format; + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/FixMeCommentCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/FixMeCommentCheck.java new file mode 100644 index 00000000..5242830c --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/FixMeCommentCheck.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.impl.TextPointerImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.sonar.check.Rule; +import org.sonarsource.analyzer.commons.TokenLocation; + +@Rule(key = "S1134") +public class FixMeCommentCheck implements SlangCheck { + + private final Pattern fixMePattern = Pattern.compile("(?i)(^|[[^\\p{L}]&&\\D])(fixme)($|[[^\\p{L}]&&\\D])"); + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> tree.allComments().forEach(comment -> { + Matcher matcher = fixMePattern.matcher(comment.text()); + if (matcher.find()) { + TextPointer start = comment.textRange().start(); + TokenLocation location = new TokenLocation( + start.line(), + start.lineOffset(), + comment.text().substring(0, matcher.start(2))); + TextRange fixMeRange = new TextRangeImpl( + new TextPointerImpl(location.endLine(), location.endLineOffset()), + new TextPointerImpl(location.endLine(), location.endLineOffset() + 5)); + ctx.reportIssue(fixMeRange, "Take the required action to fix the issue indicated by this \"FIXME\" comment."); + } + })); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/FunctionCognitiveComplexityCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/FunctionCognitiveComplexityCheck.java new file mode 100644 index 00000000..fae874a4 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/FunctionCognitiveComplexityCheck.java @@ -0,0 +1,75 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.complexity.CognitiveComplexity; +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +@Rule(key = "S3776") +public class FunctionCognitiveComplexityCheck implements SlangCheck { + + private static final int DEFAULT_THRESHOLD = 15; + + @RuleProperty( + key = "threshold", + description = "The maximum authorized complexity.", + defaultValue = "" + DEFAULT_THRESHOLD + ) + public int threshold = DEFAULT_THRESHOLD; + + @Override + public void initialize(InitContext init) { + init.register(FunctionDeclarationTree.class, (ctx, tree) -> { + if (tree.name() == null) { + return; + } + + CognitiveComplexity complexity = new CognitiveComplexity(tree); + if (complexity.value() > threshold) { + String message = String.format( + "Refactor this method to reduce its Cognitive Complexity from %s to the %s allowed.", + complexity.value(), + threshold); + List secondaryLocations = complexity.increments().stream() + .map(FunctionCognitiveComplexityCheck::secondaryLocation) + .collect(Collectors.toList()); + Double gap = (double) complexity.value() - threshold; + ctx.reportIssue(tree::rangeToHighlight, message, secondaryLocations, gap); + } + }); + } + + private static SecondaryLocation secondaryLocation(CognitiveComplexity.Increment increment) { + int nestingLevel = increment.nestingLevel(); + String message = "+" + (nestingLevel + 1); + if (nestingLevel > 0) { + message += " (incl " + nestingLevel + " for nesting)"; + } + return new SecondaryLocation(increment.token().textRange(), message); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/HardcodedCredentialsCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/HardcodedCredentialsCheck.java new file mode 100644 index 00000000..5814013d --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/HardcodedCredentialsCheck.java @@ -0,0 +1,154 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.net.URI; +import java.net.URISyntaxException; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.checks.utils.ExpressionUtils; + +import javax.annotation.Nullable; + +@Rule(key = "S2068") +public class HardcodedCredentialsCheck implements SlangCheck { + + private static final String DEFAULT_VALUE = "password,passwd,pwd,passphrase"; + private static final Pattern URI_PREFIX = Pattern.compile("^\\w{1,8}://"); + + @RuleProperty( + key = "credentialWords", + description = "Comma separated list of words identifying potential credentials", + defaultValue = DEFAULT_VALUE) + public String credentialWords = DEFAULT_VALUE; + + private List variablePatterns; + private List literalPatterns; + + @Override + public void initialize(InitContext init) { + init.register(AssignmentExpressionTree.class, (ctx, tree) -> { + Tree leftHandSide = tree.leftHandSide(); + ExpressionUtils.getMemberSelectOrIdentifierName(leftHandSide) + .ifPresent(variableName -> checkVariable(ctx, leftHandSide, variableName, tree.statementOrExpression())); + }); + + init.register(VariableDeclarationTree.class, (ctx, tree) -> + checkVariable(ctx, tree.identifier(), tree.identifier().name(), tree.initializer()) + ); + + init.register(StringLiteralTree.class, (ctx, tree) -> { + String content = tree.content(); + if (isURIWithCredentials(content)) { + ctx.reportIssue(tree, "Review this hard-coded URL, which may contain a credential."); + } else { + literalPatterns() + .map(pattern -> pattern.matcher(content)) + .filter(Matcher::find) + .map(matcher -> matcher.group(1)) + .filter(match -> !isQuery(content, match)) + .forEach(credential -> report(ctx, tree, credential)); + } + }); + } + + private static boolean isURIWithCredentials(String stringLiteral) { + if (URI_PREFIX.matcher(stringLiteral).find()) { + try { + String userInfo = new URI(stringLiteral).getUserInfo(); + if (userInfo != null) { + String[] parts = userInfo.split(":"); + return parts.length > 1 && !parts[0].equals(parts[1]); + } + } catch (URISyntaxException e) { + // ignore, stringLiteral is not a valid URI + } + } + return false; + } + + private static boolean isNotEmptyString(@Nullable Tree tree) { + return tree instanceof StringLiteralTree + && !((StringLiteralTree)tree).content().isEmpty(); + } + + private static boolean isQuery(String value, String match) { + String followingString = value.substring(value.indexOf(match) + match.length()); + return followingString.startsWith("=?") + || followingString.startsWith("=%") + || followingString.startsWith("=:") + || followingString.startsWith("={") // string format + || followingString.equals("='"); + } + + private static void report(CheckContext ctx, Tree tree, String matchName) { + String message = String.format("\"%s\" detected here, make sure this is not a hard-coded credential.", matchName); + ctx.reportIssue(tree, message); + } + + private void checkVariable(CheckContext ctx, Tree variable, String variableName, @Nullable Tree value) { + if (isNotEmptyString(value)) { + variablePatterns() + .map(pattern -> pattern.matcher(variableName)) + .filter(Matcher::find) + .forEach(matcher -> checkAssignedValue(ctx, matcher, variable, ((StringLiteralTree) value).value())); + } + } + + private static void checkAssignedValue(CheckContext ctx, Matcher matcher, Tree leftHand, String value) { + if (!matcher.pattern().matcher(value).find()) { + report(ctx, leftHand, matcher.group(1)); + } + } + + private Stream variablePatterns() { + if (variablePatterns == null) { + variablePatterns = toPatterns(""); + } + return variablePatterns.stream(); + } + + private Stream literalPatterns() { + if (literalPatterns == null) { + literalPatterns = toPatterns("=\\S"); + } + return literalPatterns.stream(); + } + + private List toPatterns(String suffix) { + return Stream.of(credentialWords.split(",")) + .map(String::trim) + .map(word -> Pattern.compile("(" + word + ")" + suffix, Pattern.CASE_INSENSITIVE)) + .collect(Collectors.toList()); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/HardcodedIpCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/HardcodedIpCheck.java new file mode 100644 index 00000000..b9a055b0 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/HardcodedIpCheck.java @@ -0,0 +1,129 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + +@Rule(key = "S1313") +public class HardcodedIpCheck implements SlangCheck { + + private static final String IPV4_ALONE = "(?(?:\\d{1,3}\\.){3}\\d{1,3})"; + + private static final String IPV6_NO_PREFIX_COMPRESSION = "(\\p{XDigit}{1,4}::?){1,7}\\p{XDigit}{1,4}(::)?"; + private static final String IPV6_PREFIX_COMPRESSION = "::((\\p{XDigit}{1,4}:){0,6}\\p{XDigit}{1,4})?"; + private static final String IPV6_ALONE = ("(?(" + IPV6_NO_PREFIX_COMPRESSION + "|" + IPV6_PREFIX_COMPRESSION + ")??(:?" + IPV4_ALONE + ")?" + ")"); + private static final String IPV6_URL = "([^\\d.]*/)?\\[" + IPV6_ALONE + "]((:\\d{1,5})?(?!\\d|\\.))(/.*)?"; + + private static final Pattern IPV4_URL_REGEX = Pattern.compile("([^\\d.]*/)?" + IPV4_ALONE + "((:\\d{1,5})?(?!\\d|\\.))(/.*)?"); + private static final List IPV6_REGEX_LIST = Arrays.asList( + Pattern.compile(IPV6_ALONE), + Pattern.compile(IPV6_URL)); + + private static final Pattern IPV6_LOOPBACK = Pattern.compile("[0:]++0*+1"); + private static final Pattern IPV6_NON_ROUTABLE = Pattern.compile("[0:]++"); + private static final Pattern INVALID_IPV4_PART_PATTERN = Pattern.compile("^0\\d{1,2}"); + + private static final List IPV6_PREFIX_EXCEPTIONS = Arrays.asList("2001:db8:", "::ffff:0:127.", "::ffff:127."); + + private static final String MESSAGE = "Make sure using this hardcoded IP address is safe here."; + + @Override + public void initialize(InitContext init) { + init.register(StringLiteralTree.class, (ctx, tree) -> { + String content = tree.content(); + Matcher matcher = IPV4_URL_REGEX.matcher(content); + if (matcher.matches()) { + String ip = matcher.group("ipv4"); + if (isValidIPV4(ip) && !isIPV4Exception(ip)) { + ctx.reportIssue(tree, MESSAGE); + } + } else { + IPV6_REGEX_LIST.stream() + .map(pattern -> pattern.matcher(content)) + .filter(Matcher::matches) + .findFirst() + .filter(match -> { + String ipv6 = match.group("ipv6"); + String ipv4 = match.group("ipv4"); + return isValidIPV6(ipv6, ipv4) && !isIPV6Exception(ipv6); + }) + .ifPresent(match -> ctx.reportIssue(tree, MESSAGE)); + } + }); + } + + private static boolean isValidIPV4(String ip) { + String[] numbersAsStrings = ip.split("\\."); + return Arrays.stream(numbersAsStrings).noneMatch( + (INVALID_IPV4_PART_PATTERN.asPredicate()) + .or(value -> Integer.valueOf(value) > 255)); + } + + private static boolean isValidIPV6(String ipv6, @Nullable String ipv4) { + String[] split = ipv6.split("::?"); + int partCount = split.length; + int compressionSeparatorCount = getCompressionSeparatorCount(ipv6); + boolean validUncompressed; + boolean validCompressed; + if (ipv4 != null) { + boolean hasValidIPV4 = isValidIPV4(ipv4); + validUncompressed = hasValidIPV4 && compressionSeparatorCount == 0 && partCount == 7; + validCompressed = hasValidIPV4 && compressionSeparatorCount == 1 && partCount <= 6; + } else { + validUncompressed = compressionSeparatorCount == 0 && partCount == 8; + validCompressed = compressionSeparatorCount == 1 && partCount <= 7; + } + + return validUncompressed || validCompressed; + } + + private static boolean isIPV4Exception(String ip) { + return ip.startsWith("127.") + || ip.startsWith("2.5.") + || ip.startsWith("192.0.2.") + || ip.startsWith("198.51.100.") + || ip.startsWith("203.0.113.") + || "255.255.255.255".equals(ip) + || "0.0.0.0".equals(ip); + } + + private static boolean isIPV6Exception(String ip) { + return IPV6_PREFIX_EXCEPTIONS.stream().anyMatch(ip::startsWith) + || IPV6_LOOPBACK.matcher(ip).matches() + || IPV6_NON_ROUTABLE.matcher(ip).matches(); + } + + private static int getCompressionSeparatorCount(String str) { + int count = 0; + for (int i = 0; (i = str.indexOf("::", i)) != -1; i += 2) { + ++count; + } + return count; + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/IdenticalBinaryOperandCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/IdenticalBinaryOperandCheck.java new file mode 100644 index 00000000..493ff7aa --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/IdenticalBinaryOperandCheck.java @@ -0,0 +1,50 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonar.check.Rule; + +import static org.sonarsource.slang.checks.utils.ExpressionUtils.containsPlaceHolder; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.skipParentheses; +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; + +@Rule(key = "S1764") +public class IdenticalBinaryOperandCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(BinaryExpressionTree.class, (ctx, tree) -> { + if (tree.operator() != BinaryExpressionTree.Operator.PLUS + && tree.operator() != BinaryExpressionTree.Operator.TIMES + && !containsPlaceHolder(tree) + && areEquivalent(skipParentheses(tree.leftOperand()), skipParentheses(tree.rightOperand()))) { + ctx.reportIssue( + tree.rightOperand(), + "Correct one of the identical sub-expressions on both sides this operator", + new SecondaryLocation(tree.leftOperand())); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/IdenticalConditionsCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/IdenticalConditionsCheck.java new file mode 100644 index 00000000..2d18fce4 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/IdenticalConditionsCheck.java @@ -0,0 +1,84 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.ExpressionUtils; +import org.sonarsource.slang.utils.SyntacticEquivalence; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.sonar.check.Rule; + +import static org.sonarsource.slang.checks.utils.ExpressionUtils.skipParentheses; + +@Rule(key = "S1862") +public class IdenticalConditionsCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(MatchTree.class, (ctx, tree) -> checkConditions(ctx, collectConditions(tree))); + init.register(IfTree.class, (ctx, tree) -> { + if (!(ctx.parent() instanceof IfTree)) { + checkConditions(ctx, collectConditions(tree, new ArrayList<>())); + } + }); + } + + private static List collectConditions(MatchTree matchTree) { + return matchTree.cases().stream() + .map(MatchCaseTree::expression) + .filter(Objects::nonNull) + .map(ExpressionUtils::skipParentheses) + .collect(Collectors.toList()); + } + + private static List collectConditions(IfTree ifTree, List list) { + list.add(skipParentheses(ifTree.condition())); + Tree elseBranch = ifTree.elseBranch(); + if (elseBranch instanceof IfTree) { + return collectConditions((IfTree) elseBranch, list); + } + return list; + } + + private static void checkConditions(CheckContext ctx, List conditions) { + for (List group : SyntacticEquivalence.findDuplicatedGroups(conditions)) { + Tree original = group.get(0); + group.stream().skip(1) + .forEach(duplicated -> { + TextRange originalRange = original.metaData().textRange(); + ctx.reportIssue( + duplicated, + "This condition duplicates the one on line " + originalRange.start().line() + ".", + new SecondaryLocation(originalRange, "Original")); + }); + } + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalseCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalseCheck.java new file mode 100644 index 00000000..3660a8fe --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalseCheck.java @@ -0,0 +1,79 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.BinaryExpressionTree.Operator; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.ExpressionUtils; +import java.util.function.Predicate; +import org.sonar.check.Rule; + +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.CONDITIONAL_AND; +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.CONDITIONAL_OR; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isBinaryOperation; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isBooleanLiteral; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isFalseValueLiteral; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isNegation; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isTrueValueLiteral; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.skipParentheses; + +@Rule(key = "S1145") +public class IfConditionalAlwaysTrueOrFalseCheck implements SlangCheck { + + public static final String MESSAGE_TEMPLATE = "Remove this useless \"%s\" statement."; + + @Override + public void initialize(InitContext init) { + init.register(IfTree.class, (ctx, ifTree) -> { + Tree condition = ifTree.condition(); + if (isAlwaysTrueOrFalse(condition)) { + String message = String.format(MESSAGE_TEMPLATE, ifTree.ifKeyword().text()); + ctx.reportIssue(condition, message); + } + }); + } + + private static boolean isAlwaysTrueOrFalse(Tree originalCondition) { + Tree condition = skipParentheses(originalCondition); + return isBooleanLiteral(condition) + || isTrueValueLiteral(condition) + || isFalseValueLiteral(condition) + || isSimpleExpressionWithLiteral(condition, CONDITIONAL_AND, ExpressionUtils::isFalseValueLiteral) + || isSimpleExpressionWithLiteral(condition, CONDITIONAL_OR, ExpressionUtils::isTrueValueLiteral); + } + + private static boolean isSimpleExpressionWithLiteral(Tree condition, Operator operator, Predicate hasLiteralValue) { + boolean simpleExpression = isBinaryOperation(condition, operator) + && condition.descendants() + .map(ExpressionUtils::skipParentheses) + .allMatch(tree -> tree instanceof IdentifierTree + || tree instanceof LiteralTree + || isNegation(tree) + || isBinaryOperation(tree, operator)); + + return simpleExpression && condition.descendants().anyMatch(hasLiteralValue); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/MatchCaseTooBigCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/MatchCaseTooBigCheck.java new file mode 100644 index 00000000..fcd0c89c --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/MatchCaseTooBigCheck.java @@ -0,0 +1,58 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.text.MessageFormat; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; + +@Rule(key = "S1151") +public class MatchCaseTooBigCheck implements SlangCheck { + + private static final int DEFAULT_MAX = 15; + private static final String DEFAULT_MAX_VALUE = "" + DEFAULT_MAX; + + private static final String MESSAGE = + "Reduce this case clause number of lines from {0} to at most {1}, for example by extracting code into methods."; + + @RuleProperty( + key = "max", + description = "Maximum number of lines" + ) + @PropertyDefaultValue(language = Language.RUBY, defaultValue = DEFAULT_MAX_VALUE) + @PropertyDefaultValue(language = Language.SCALA, defaultValue = DEFAULT_MAX_VALUE) + @PropertyDefaultValue(language = Language.GO, defaultValue = "" + Language.GO_MATCH_CASES_DEFAULT_MAX) + public int max = DEFAULT_MAX; + + @Override + public void initialize(InitContext init) { + init.register(MatchCaseTree.class, (ctx, matchCaseTree) -> { + int linesOfCode = matchCaseTree.metaData().linesOfCode().size(); + if (linesOfCode > max) { + ctx.reportIssue(matchCaseTree.rangeToHighlight(), MessageFormat.format(MESSAGE, linesOfCode, max)); + } + }); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/MatchWithoutElseCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/MatchWithoutElseCheck.java new file mode 100644 index 00000000..22dc0228 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/MatchWithoutElseCheck.java @@ -0,0 +1,43 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + + +import org.sonar.check.Rule; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + +@Rule(key = "S131") +public class MatchWithoutElseCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(MatchTree.class, (ctx, tree) -> { + if (tree.cases().stream().noneMatch(matchCase -> matchCase.expression() == null)) { + Token keyword = tree.keyword(); + String message = String.format("Add a default clause to this \"%s\" statement.", keyword.text()); + ctx.reportIssue(keyword, message); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/NestedMatchCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/NestedMatchCheck.java new file mode 100644 index 00000000..d1c91c28 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/NestedMatchCheck.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.text.MessageFormat; +import org.sonar.check.Rule; + +@Rule(key = "S1821") +public class NestedMatchCheck implements SlangCheck { + private static final String MESSAGE = "Refactor the code to eliminate this nested \"{0}\"."; + + @Override + public void initialize(InitContext init) { + init.register(MatchTree.class, (ctx, matchTree) -> ctx.ancestors().stream() + .filter(MatchTree.class::isInstance) + .findFirst() + .ifPresent(parentMatch -> + ctx.reportIssue(matchTree.keyword(), MessageFormat.format(MESSAGE, matchTree.keyword().text())) + )); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/OctalValuesCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/OctalValuesCheck.java new file mode 100644 index 00000000..7c3ac5be --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/OctalValuesCheck.java @@ -0,0 +1,52 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.math.BigInteger; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.IntegerLiteralTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + +import static org.sonarsource.slang.api.IntegerLiteralTree.Base.OCTAL; + +@Rule(key = "S1314") +public class OctalValuesCheck implements SlangCheck { + + private static final String MESSAGE = "Use decimal values instead of octal ones."; + private static final BigInteger EIGHT = BigInteger.valueOf(OCTAL.getRadix()); + private static final int FILE_PERMISSION_MASK_LENGTH = 3; + + @Override + public void initialize(InitContext init) { + init.register(IntegerLiteralTree.class, (ctx, literal) -> { + if (literal.getBase() == OCTAL && !isException(literal)) { + ctx.reportIssue(literal, MESSAGE); + } + }); + } + + private static boolean isException(IntegerLiteralTree literalTree) { + // octal literal < 8 are authorized, as well as octal literals with 3 digits, as they are often used for file permissions + BigInteger value = literalTree.getIntegerValue(); + return value.compareTo(EIGHT) < 0 || literalTree.getNumericPart().length() == FILE_PERMISSION_MASK_LENGTH; + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/OneStatementPerLineCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/OneStatementPerLineCheck.java new file mode 100644 index 00000000..0bccedc9 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/OneStatementPerLineCheck.java @@ -0,0 +1,73 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.check.Rule; + +@Rule(key = "S122") +public class OneStatementPerLineCheck implements SlangCheck { + private static final String MESSAGE = "Reformat the code to have only one statement per line."; + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, topLevelTree) -> checkStatements(ctx, topLevelTree.children())); + init.register(BlockTree.class, (ctx, blockTree) -> checkStatements(ctx, blockTree.statementOrExpressions())); + } + + protected boolean shouldIgnore(Tree tree) { + return false; + } + + private void checkStatements(CheckContext ctx, List statementsOrExpressions) { + statementsOrExpressions.stream() + .filter(tree -> !shouldIgnore(tree)) + .collect(Collectors.groupingBy(OneStatementPerLineCheck::getLine)) + .forEach((line, statements) -> { + if (statements.size() > 1) { + reportIssue(ctx, statements); + } + }); + } + + private static void reportIssue(CheckContext ctx, List statements) { + List secondaryLocations = statements.stream() + .skip(2) + .map(statement -> new SecondaryLocation(statement, null)) + .collect(Collectors.toList()); + ctx.reportIssue( + statements.get(1), + MESSAGE, + secondaryLocations + ); + } + + private static int getLine(Tree statementOrExpression) { + return statementOrExpression.metaData().textRange().start().line(); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/ParsingErrorCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/ParsingErrorCheck.java new file mode 100644 index 00000000..2905ffca --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/ParsingErrorCheck.java @@ -0,0 +1,32 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonar.check.Rule; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + +@Rule(key = "ParsingError") +public class ParsingErrorCheck implements SlangCheck { + @Override + public void initialize(InitContext init) { + // errors are reported in InputFileContext#reportParseError + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/RedundantParenthesesCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/RedundantParenthesesCheck.java new file mode 100644 index 00000000..75f45616 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/RedundantParenthesesCheck.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.ParenthesizedExpressionTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonar.check.Rule; + +@Rule(key = "S1110") +public class RedundantParenthesesCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(ParenthesizedExpressionTree.class, (ctx, tree) -> { + if (ctx.parent() instanceof ParenthesizedExpressionTree) { + SecondaryLocation secondaryLocation = new SecondaryLocation(tree.rightParenthesis().textRange(), null); + ctx.reportIssue(tree.leftParenthesis(), "Remove these useless parentheses.", secondaryLocation); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/SelfAssignmentCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/SelfAssignmentCheck.java new file mode 100644 index 00000000..a88ab3a8 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/SelfAssignmentCheck.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonar.check.Rule; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; + +@Rule(key = "S1656") +public class SelfAssignmentCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(AssignmentExpressionTree.class, (ctx, tree) -> { + if (tree.operator() == AssignmentExpressionTree.Operator.EQUAL && areEquivalent(tree.leftHandSide(), tree.statementOrExpression())) { + ctx.reportIssue(tree, "Remove or correct this useless self-assignment."); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/StringLiteralDuplicatedCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/StringLiteralDuplicatedCheck.java new file mode 100644 index 00000000..1b926117 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/StringLiteralDuplicatedCheck.java @@ -0,0 +1,80 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +@Rule(key = "S1192") +public class StringLiteralDuplicatedCheck implements SlangCheck { + + private static final int DEFAULT_THRESHOLD = 3; + private static final int MINIMAL_LITERAL_LENGTH = 5; + private static final Pattern NO_SEPARATOR_REGEXP = Pattern.compile("\\w++"); + + @RuleProperty( + key = "threshold", + description = "Number of times a literal must be duplicated to trigger an issue", + defaultValue = "" + DEFAULT_THRESHOLD) + public int threshold = DEFAULT_THRESHOLD; + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> { + Map> occurrences = new HashMap<>(); + tree.descendants() + .filter(StringLiteralTree.class::isInstance) + .map(StringLiteralTree.class::cast) + .filter(literal -> literal.content().length() > MINIMAL_LITERAL_LENGTH && !NO_SEPARATOR_REGEXP.matcher(literal.content()).matches()) + .forEach(literal -> occurrences.computeIfAbsent(literal.content(), key -> new LinkedList<>()).add(literal)); + check(ctx, occurrences, threshold); + }); + } + + private static void check(CheckContext ctx, Map> occurrencesMap, int threshold) { + for (Map.Entry> entry : occurrencesMap.entrySet()) { + List occurrences = entry.getValue(); + int size = occurrences.size(); + if (size >= threshold) { + StringLiteralTree first = occurrences.get(0); + String message = String.format("Define a constant instead of duplicating this literal \"%s\" %s times.", first.content(), size); + List secondaryLocations = occurrences.stream() + .skip(1) + .map(stringLiteral -> new SecondaryLocation(stringLiteral.metaData().textRange(), "Duplication")) + .collect(Collectors.toList()); + double gap = size - 1.0; + ctx.reportIssue(first, message, secondaryLocations, gap); + } + } + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TabsCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TabsCheck.java new file mode 100644 index 00000000..ab0c1578 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TabsCheck.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonar.check.Rule; + +@Rule(key = "S105") +public class TabsCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> { + String fileContent = ctx.fileContent(); + if (fileContent.contains("\t")) { + String message = String.format("Replace all tab characters in this file \"%s\" by sequences of white-spaces.", ctx.filename()); + ctx.reportFileIssue(message); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TodoCommentCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TodoCommentCheck.java new file mode 100644 index 00000000..63ca7a89 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TodoCommentCheck.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.impl.TextPointerImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.sonar.check.Rule; +import org.sonarsource.analyzer.commons.TokenLocation; + +@Rule(key = "S1135") +public class TodoCommentCheck implements SlangCheck { + + private final Pattern todoPattern = Pattern.compile("(?i)(^|[[^\\p{L}]&&\\D])(todo)($|[[^\\p{L}]&&\\D])"); + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> + tree.allComments().forEach(comment -> { + Matcher matcher = todoPattern.matcher(comment.text()); + if (matcher.find()) { + TextPointer start = comment.textRange().start(); + TokenLocation location = new TokenLocation(start.line(), start.lineOffset(), comment.text().substring(0, matcher.start(2))); + TextRange todoRange = new TextRangeImpl( + new TextPointerImpl(location.endLine(), location.endLineOffset()), + new TextPointerImpl(location.endLine(), location.endLineOffset() + 4) + ); + ctx.reportIssue(todoRange, "Complete the task associated to this TODO comment."); + } + }) + ); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TooComplexExpressionCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooComplexExpressionCheck.java new file mode 100644 index 00000000..d021175b --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooComplexExpressionCheck.java @@ -0,0 +1,94 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.ParenthesizedExpressionTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.Collections; +import java.util.Iterator; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isLogicalBinaryExpression; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.skipParentheses; + +@Rule(key = "S1067") +public class TooComplexExpressionCheck implements SlangCheck { + + private static final int DEFAULT_MAX_COMPLEXITY = 3; + + @RuleProperty(key = "max", + description = "Maximum number of allowed conditional operators in an expression", + defaultValue = "" + DEFAULT_MAX_COMPLEXITY) + public int max = DEFAULT_MAX_COMPLEXITY; + + @Override + public void initialize(InitContext init) { + init.register(BinaryExpressionTree.class, (ctx, tree) -> { + if (isParentExpression(ctx)) { + int complexity = computeExpressionComplexity(tree); + if (complexity > max) { + String message = String.format( + "Reduce the number of conditional operators (%s) used in the expression (maximum allowed %s).", + complexity, + max); + double gap = (double) complexity - max; + ctx.reportIssue(tree, message, Collections.emptyList(), gap); + } + } + }); + } + + private static boolean isParentExpression(CheckContext ctx) { + Iterator iterator = ctx.ancestors().iterator(); + while (iterator.hasNext()) { + Tree parentExpression = iterator.next(); + if (parentExpression instanceof BinaryExpressionTree) { + return false; + } else if (!(parentExpression instanceof UnaryExpressionTree) + // TODO(Godin): seems that instead of logical-or should be logical-and + || !(parentExpression instanceof ParenthesizedExpressionTree)) { + return true; + } + } + return true; + } + + private static int computeExpressionComplexity(Tree originalTree) { + Tree tree = skipParentheses(originalTree); + if (tree instanceof BinaryExpressionTree) { + int complexity = isLogicalBinaryExpression(tree) ? 1 : 0; + BinaryExpressionTree binary = (BinaryExpressionTree) tree; + return complexity + + computeExpressionComplexity(binary.leftOperand()) + + computeExpressionComplexity(binary.rightOperand()); + } else if (tree instanceof UnaryExpressionTree) { + return computeExpressionComplexity(((UnaryExpressionTree) tree).operand()); + } else { + return 0; + } + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TooDeeplyNestedStatementsCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooDeeplyNestedStatementsCheck.java new file mode 100644 index 00000000..86dd270e --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooDeeplyNestedStatementsCheck.java @@ -0,0 +1,130 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import javax.annotation.Nullable; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.api.ExceptionHandlingTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.ExpressionUtils; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; + +@Rule(key = "S134") +public class TooDeeplyNestedStatementsCheck implements SlangCheck { + private static final int DEFAULT_MAX_DEPTH = 3; + private static final String DEFAULT_MAX_DEPTH_VALUE = "" + DEFAULT_MAX_DEPTH; + + @RuleProperty( + key = "max", + description = "Maximum allowed control flow statement nesting depth" + ) + @PropertyDefaultValue(language = Language.RUBY, defaultValue = DEFAULT_MAX_DEPTH_VALUE) + @PropertyDefaultValue(language = Language.SCALA, defaultValue = DEFAULT_MAX_DEPTH_VALUE) + @PropertyDefaultValue(language = Language.GO, defaultValue = "" + Language.GO_NESTED_STATEMENT_MAX_DEPTH) + public int max = DEFAULT_MAX_DEPTH; + + @Override + public void initialize(InitContext init) { + init.register(IfTree.class, this::checkNestedDepth); + init.register(LoopTree.class, this::checkNestedDepth); + init.register(MatchTree.class, this::checkNestedDepth); + init.register(ExceptionHandlingTree.class, this::checkNestedDepth); + } + + private void checkNestedDepth(CheckContext ctx, Tree tree) { + if (isElseIfStatement(ctx.parent(), tree)) { + // Ignore 'else-if' statements since the issue would already be raised on the first 'if' statement + return; + } + if (ExpressionUtils.isTernaryOperator(ctx.ancestors(), tree)) { + return; + } + + Iterator iterator = ctx.ancestors().iterator(); + Deque nestedParentNodes = new LinkedList<>(); + Tree last = tree; + + while (iterator.hasNext()) { + Tree parent = iterator.next(); + if (isElseIfStatement(parent, last) && !nestedParentNodes.isEmpty()) { + // Only the 'if' parent of the chained 'else-if' statements should be highlighted + nestedParentNodes.removeLast(); + } + if (parent instanceof LoopTree || parent instanceof ExceptionHandlingTree || parent instanceof IfTree || parent instanceof MatchTree) { + nestedParentNodes.addLast(getNodeToHighlight(parent)); + } + if (nestedParentNodes.size() > max) { + return; + } + last = parent; + } + + if (nestedParentNodes.size() == max) { + reportIssue(ctx, tree, nestedParentNodes); + } + } + + private static boolean isElseIfStatement(@Nullable Tree parent, @Nullable Tree tree) { + return tree instanceof IfTree && parent instanceof IfTree && tree.equals(((IfTree) parent).elseBranch()); + } + + private void reportIssue(CheckContext ctx, Tree statement, Deque nestedStatements) { + String message = String.format("Refactor this code to not nest more than %s control flow statements.", max); + List secondaryLocations = new ArrayList<>(nestedStatements.size()); + int nestedDepth = 0; + + while (!nestedStatements.isEmpty()) { + nestedDepth++; + String secondaryLocationMessage = String.format("Nesting depth %s", nestedDepth); + secondaryLocations.add(new SecondaryLocation(nestedStatements.removeLast().textRange(), secondaryLocationMessage)); + } + + Token nodeToHighlight = getNodeToHighlight(statement); + ctx.reportIssue(nodeToHighlight, message, secondaryLocations); + } + + private static Token getNodeToHighlight(Tree tree) { + if (tree instanceof IfTree) { + return ((IfTree) tree).ifKeyword(); + } else if (tree instanceof MatchTree) { + return ((MatchTree) tree).keyword(); + } else if (tree instanceof ExceptionHandlingTree) { + return ((ExceptionHandlingTree) tree).tryKeyword(); + } else { + return ((LoopTree) tree).keyword(); + } + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TooLongFunctionCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooLongFunctionCheck.java new file mode 100644 index 00000000..2fc359ae --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooLongFunctionCheck.java @@ -0,0 +1,64 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; + +@Rule(key = "S138") +public class TooLongFunctionCheck implements SlangCheck { + + private static final int DEFAULT_MAX = 100; + private static final String DEFAULT_MAX_VALUE = "" + DEFAULT_MAX; + + @RuleProperty( + key = "max", + description = "Maximum authorized lines of code in a function" + ) + @PropertyDefaultValue(language = Language.RUBY, defaultValue = DEFAULT_MAX_VALUE) + @PropertyDefaultValue(language = Language.SCALA, defaultValue = DEFAULT_MAX_VALUE) + @PropertyDefaultValue(language = Language.GO, defaultValue = "" + Language.GO_DEFAULT_MAXIMUM_FUNCTION_LENGTH) + public int max = DEFAULT_MAX; + + @Override + public void initialize(InitContext init) { + init.register(FunctionDeclarationTree.class, (ctx, tree) -> { + BlockTree body = tree.body(); + if (body == null) { + return; + } + int numberOfLinesOfCode = body.metaData().linesOfCode().size(); + if (numberOfLinesOfCode > max) { + String message = String.format( + "This function has %s lines of code, which is greater than the %s authorized. Split it into smaller functions.", + numberOfLinesOfCode, + max); + ctx.reportIssue(tree.rangeToHighlight(), message); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TooLongLineCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooLongLineCheck.java new file mode 100644 index 00000000..de359296 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooLongLineCheck.java @@ -0,0 +1,71 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; +import org.sonarsource.slang.impl.TextPointerImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import java.text.MessageFormat; +import java.util.stream.IntStream; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +@Rule(key = "S103") +public class TooLongLineCheck implements SlangCheck { + private static final int DEFAULT_MAXIMUM_LINE_LENGTH = 200; + private static final String DEFAULT_MAXIMUM_LINE_LENGTH_VALUE= "" + DEFAULT_MAXIMUM_LINE_LENGTH; + + @RuleProperty( + key = "maximumLineLength", + description = "The maximum authorized line length." + ) + @PropertyDefaultValue(language = Language.RUBY, defaultValue = DEFAULT_MAXIMUM_LINE_LENGTH_VALUE) + @PropertyDefaultValue(language = Language.SCALA, defaultValue = DEFAULT_MAXIMUM_LINE_LENGTH_VALUE) + @PropertyDefaultValue(language = Language.GO, defaultValue = "" + Language.GO_DEFAULT_MAXIMUM_LINE_LENGTH) + int maximumLineLength = DEFAULT_MAXIMUM_LINE_LENGTH; + + private static final String MESSAGE = "Split this {0} characters long line (which is greater than {1} authorized)."; + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, ((ctx, topLevelTree) -> { + String[] lines = ctx.fileContent().split("\r\n|\n|\r", -1); + IntStream.range(0, lines.length) + .filter(lineNumber -> lines[lineNumber].length() > maximumLineLength) + .forEach(lineNumber -> { + int lineLength = lines[lineNumber].length(); + TextRange longLine = getLineRange(lineNumber + 1, lineLength); + ctx.reportIssue(longLine, MessageFormat.format(MESSAGE, lineLength, maximumLineLength)); + }); + })); + } + + private static TextRange getLineRange(int lineNumber, int lineLength) { + return new TextRangeImpl( + new TextPointerImpl(lineNumber, 0), + new TextPointerImpl(lineNumber, lineLength) + ); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyCasesCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyCasesCheck.java new file mode 100644 index 00000000..4fc67d66 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyCasesCheck.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; + +@Rule(key = "S1479") +public class TooManyCasesCheck implements SlangCheck { + + private static final int DEFAULT_MAX = 30; + + @RuleProperty( + key = "maximum", + description = "Maximum number of branches", + defaultValue = "" + DEFAULT_MAX) + public int maximum = DEFAULT_MAX; + + @Override + public void initialize(InitContext init) { + init.register(MatchTree.class, (ctx, tree) -> { + int numberOfCases = tree.cases().size(); + if (numberOfCases > maximum) { + Token matchKeyword = tree.keyword(); + String message = String.format( + "Reduce the number of %s branches from %s to at most %s.", + matchKeyword.text(), + numberOfCases, + maximum); + List secondaryLocations = tree.cases().stream() + .map(matchCase -> new SecondaryLocation(matchCase.rangeToHighlight(), null)) + .collect(Collectors.toList()); + ctx.reportIssue(matchKeyword, message, secondaryLocations); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyLinesOfCodeFileCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyLinesOfCodeFileCheck.java new file mode 100644 index 00000000..2e8003f3 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyLinesOfCodeFileCheck.java @@ -0,0 +1,59 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; + +@Rule(key = "S104") +public class TooManyLinesOfCodeFileCheck implements SlangCheck { + + private static final int DEFAULT_MAX = 1000; + private static final String DEFAULT_MAX_VALUE = "" + DEFAULT_MAX; + + + @RuleProperty( + key = "Max", + description = "Maximum authorized lines of code in a file." + ) + @PropertyDefaultValue(language = Language.RUBY, defaultValue = DEFAULT_MAX_VALUE) + @PropertyDefaultValue(language = Language.SCALA, defaultValue = DEFAULT_MAX_VALUE) + @PropertyDefaultValue(language = Language.GO, defaultValue = "" + Language.GO_DEFAULT_FILE_LINE_MAX) + public int max = DEFAULT_MAX; + + @Override + public void initialize(InitContext init) { + init.register(TopLevelTree.class, (ctx, tree) -> { + int numberOfLinesOfCode = tree.metaData().linesOfCode().size(); + if (numberOfLinesOfCode > max) { + String message = String.format( + "File \"%s\" has %s lines, which is greater than %s authorized. Split it into smaller files.", + ctx.filename(), numberOfLinesOfCode, max); + ctx.reportFileIssue(message); + } + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyParametersCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyParametersCheck.java new file mode 100644 index 00000000..8b2a4d27 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/TooManyParametersCheck.java @@ -0,0 +1,81 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; + +@Rule(key = "S107") +public class TooManyParametersCheck implements SlangCheck { + + private static final int DEFAULT_MAX = 7; + + @RuleProperty( + key = "Max", + description = "Maximum authorized number of parameters", + defaultValue = "" + DEFAULT_MAX + ) + public int max = DEFAULT_MAX; + + @Override + public void initialize(InitContext init) { + init.register(FunctionDeclarationTree.class, (ctx, tree) -> { + if (isCandidateMethod(tree)) { + String message = String.format( + "This function has %s parameters, which is greater than the %s authorized.", + tree.formalParameters().size(), + max); + List secondaryLocations = tree.formalParameters().stream() + .skip(max) + .map(SecondaryLocation::new) + .collect(Collectors.toList()); + + if (tree.name() == null) { + ctx.reportIssue(tree, message, secondaryLocations); + } else { + ctx.reportIssue(tree.name(), message, secondaryLocations); + } + } + }); + } + + protected boolean isCandidateMethod(FunctionDeclarationTree functionDeclarationTree) { + return !functionDeclarationTree.isConstructor() + && !isOverrideMethod(functionDeclarationTree) + && functionDeclarationTree.formalParameters().size() > max; + } + + private static boolean isOverrideMethod(FunctionDeclarationTree tree) { + return tree.modifiers().stream().anyMatch(mod -> { + if (!(mod instanceof ModifierTree)) { + return false; + } + return ((ModifierTree) mod).kind() == ModifierTree.Kind.OVERRIDE; + }); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedFunctionParameterCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedFunctionParameterCheck.java new file mode 100644 index 00000000..e81a6765 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedFunctionParameterCheck.java @@ -0,0 +1,108 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.List; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; + +import static org.sonarsource.slang.checks.utils.FunctionUtils.isOverrideMethod; +import static org.sonarsource.slang.checks.utils.FunctionUtils.isPrivateMethod; +import static org.sonarsource.slang.utils.SyntacticEquivalence.areEquivalent; + +@Rule(key = "S1172") +public class UnusedFunctionParameterCheck implements SlangCheck { + + // Currently we ignore all functions named "main", however this should be configurable based on the analyzed language in the future. + protected static final Pattern IGNORED_PATTERN = Pattern.compile("main", Pattern.CASE_INSENSITIVE); + + @Override + public void initialize(InitContext init) { + init.register(FunctionDeclarationTree.class, (ctx, functionDeclarationTree) -> { + if (functionDeclarationTree.isConstructor() || shouldBeIgnored(ctx, functionDeclarationTree)) { + return; + } + + List unusedParameters = getUnusedParameters(functionDeclarationTree); + + if (unusedParameters.isEmpty()) { + return; + } + + reportUnusedParameters(ctx, unusedParameters); + }); + } + + protected static List getUnusedParameters(FunctionDeclarationTree functionDeclarationTree) { + return functionDeclarationTree.formalParameters().stream() + .filter(ParameterTree.class::isInstance) + .map(ParameterTree.class::cast) + .filter(parameterTree -> parameterTree.modifiers().isEmpty() && functionDeclarationTree.descendants() + .noneMatch(tree -> !tree.equals(parameterTree.identifier()) && areEquivalent(tree, parameterTree.identifier()))) + .collect(Collectors.toList()); + } + + protected void reportUnusedParameters(CheckContext ctx, List unusedParameters) { + List secondaryLocations = unusedParameters.stream() + .map(unusedParameter -> { + // Identifier can be null only in case of Scala. In that case modifiers like 'using' should be used. + // If modifiers are present, such parameters won't be checked by this rule. + IdentifierTree identifier = unusedParameter.identifier(); + Objects.requireNonNull(identifier, "Identifier for an unused parameter is null"); + return new SecondaryLocation(identifier, "Remove this unused method parameter " + identifier.name() + "\"."); + }) + .collect(Collectors.toList()); + + IdentifierTree firstUnused = unusedParameters.get(0).identifier(); + Objects.requireNonNull(firstUnused, "Identifier for an unused parameter is null"); + + String msg; + if (unusedParameters.size() > 1) { + msg = "Remove these unused function parameters."; + } else { + msg = "Remove this unused function parameter \"" + firstUnused.name() + "\"."; + } + + ctx.reportIssue(firstUnused, msg, secondaryLocations); + } + + protected boolean isValidFunctionForRule(CheckContext ctx, FunctionDeclarationTree tree) { + return ctx.parent() instanceof TopLevelTree || (isPrivateMethod(tree) && !isOverrideMethod(tree)); + } + + protected boolean shouldBeIgnored(CheckContext ctx, FunctionDeclarationTree tree) { + IdentifierTree name = tree.name(); + boolean validFunctionForRule = isValidFunctionForRule(ctx, tree); + return !validFunctionForRule + || tree.body() == null + || (name != null && IGNORED_PATTERN.matcher(name.name()).matches()); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedLocalVariableCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedLocalVariableCheck.java new file mode 100644 index 00000000..d8a9a08b --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedLocalVariableCheck.java @@ -0,0 +1,67 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.utils.SyntacticEquivalence; +import org.sonar.check.Rule; + +import java.util.Set; +import java.util.stream.Collectors; + +@Rule(key = "S1481") +public class UnusedLocalVariableCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(FunctionDeclarationTree.class, (ctx, functionDeclarationTree) -> { + + if(ctx.ancestors().stream().anyMatch(FunctionDeclarationTree.class::isInstance)) { + return; + } + + Set variableIdentifiers = getVariableIdentifierTrees(functionDeclarationTree); + Set identifierTrees = getIdentifierTrees(functionDeclarationTree, variableIdentifiers); + + variableIdentifiers.stream() + .filter(variable -> identifierTrees.stream().noneMatch(identifier -> SyntacticEquivalence.areEquivalent(variable, identifier))) + .forEach(identifier -> ctx.reportIssue(identifier, "Remove this unused \"" + identifier.name() + "\" local variable.")); + }); + } + + protected Set getVariableIdentifierTrees(FunctionDeclarationTree functionDeclarationTree) { + return functionDeclarationTree.descendants() + .filter(VariableDeclarationTree.class::isInstance) + .map(VariableDeclarationTree.class::cast) + .map(VariableDeclarationTree::identifier) + .collect(Collectors.toSet()); + } + + protected Set getIdentifierTrees(FunctionDeclarationTree functionDeclarationTree, Set variableIdentifiers) { + return functionDeclarationTree.descendants() + .filter(tree -> !variableIdentifiers.contains(tree)) + .collect(Collectors.toSet()); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedPrivateMethodCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedPrivateMethodCheck.java new file mode 100644 index 00000000..6422e80f --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/UnusedPrivateMethodCheck.java @@ -0,0 +1,108 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.FunctionUtils; +import org.sonarsource.slang.utils.SyntacticEquivalence; + +import static org.sonarsource.slang.utils.SyntacticEquivalence.getUniqueIdentifier; + +@Rule(key = "S1144") +public class UnusedPrivateMethodCheck implements SlangCheck { + + @Override + public void initialize(InitContext init) { + init.register(ClassDeclarationTree.class, this::processClassDeclaration); + } + + protected void processClassDeclaration(CheckContext context, ClassDeclarationTree classDeclarationTree) { + // only verify the outermost class in the file, to avoid raising the same issue multiple times + if (context.ancestors().stream().noneMatch(ClassDeclarationTree.class::isInstance)) { + reportUnusedPrivateMethods(context, classDeclarationTree); + } + } + + protected void reportUnusedPrivateMethods(CheckContext context, ClassDeclarationTree classDeclarationTree) { + MethodAndIdentifierCollector methodAndIdentifierCollector = new MethodAndIdentifierCollector(classDeclarationTree.descendants()); + methodAndIdentifierCollector.getMethodDeclarations().stream() + .filter(this::isValidPrivateMethod) + .forEach(tree -> { + IdentifierTree identifier = tree.name(); + if (identifier != null && isUnusedMethod(identifier, methodAndIdentifierCollector.getUsedUniqueIdentifiers())) { + String message = String.format("Remove this unused private \"%s\" method.", identifier.name()); + context.reportIssue(tree.rangeToHighlight(), message); + } + }); + } + + protected boolean isValidPrivateMethod(FunctionDeclarationTree method) { + return FunctionUtils.isPrivateMethod(method) && !FunctionUtils.isOverrideMethod(method); + } + + protected boolean isUnusedMethod(IdentifierTree identifier, Set usedIdentifierNames) { + return !usedIdentifierNames.contains(getUniqueIdentifier(identifier)); + } + + protected static class MethodAndIdentifierCollector { + private Set methodDeclarations = new HashSet<>(); + private Set usedUniqueIdentifiers; + + Set getMethodDeclarations() { + return methodDeclarations; + } + public Set getUsedUniqueIdentifiers() { + return usedUniqueIdentifiers; + } + + public MethodAndIdentifierCollector(Stream descendants) { + Set usedIdentifiers = new HashSet<>(); + descendants.forEach(tree -> { + if (tree instanceof FunctionDeclarationTree && !((FunctionDeclarationTree)tree).isConstructor()) { + methodDeclarations.add(((FunctionDeclarationTree) tree)); + } else if (tree instanceof IdentifierTree) { + usedIdentifiers.add((IdentifierTree) tree); + } + }); + + usedIdentifiers.removeAll(methodDeclarations.stream() + .map(FunctionDeclarationTree::name) + .collect(Collectors.toSet())); + + usedUniqueIdentifiers = usedIdentifiers.stream() + .filter(Objects::nonNull) + .map(SyntacticEquivalence::getUniqueIdentifier) + .collect(Collectors.toCollection(HashSet::new)); + } + + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/VariableAndParameterNameCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/VariableAndParameterNameCheck.java new file mode 100644 index 00000000..258430d9 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/VariableAndParameterNameCheck.java @@ -0,0 +1,74 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; + +@Rule(key = "S117") +public class VariableAndParameterNameCheck implements SlangCheck { + + private static final String DEFAULT_FORMAT = "^[_a-z][a-zA-Z0-9]*$"; + + @RuleProperty( + key = "format", + description = "Regular expression used to check the names against." + ) + @PropertyDefaultValue(language = Language.RUBY, defaultValue = Language.RUBY_NAMING_DEFAULT) + @PropertyDefaultValue(language = Language.SCALA, defaultValue = Language.SCALA_NAMING_DEFAULT) + @PropertyDefaultValue(language = Language.GO, defaultValue = Language.GO_NAMING_DEFAULT) + public String format = DEFAULT_FORMAT; + + @Override + public void initialize(InitContext init) { + Pattern pattern = Pattern.compile(format); + + init.register(VariableDeclarationTree.class, (ctx, tree) -> { + if (ctx.ancestors().stream().anyMatch(FunctionDeclarationTree.class::isInstance)) { + check(pattern, ctx, tree.identifier(), "local variable"); + } + }); + + init.register(FunctionDeclarationTree.class, (ctx, tree) -> + tree.formalParameters().stream() + .filter(ParameterTree.class::isInstance) + .map(ParameterTree.class::cast) + .forEach(param -> check(pattern, ctx, param.identifier(), "parameter"))); + } + + private void check(Pattern pattern, CheckContext ctx, @Nullable IdentifierTree identifier, String variableKind) { + if (identifier != null && !pattern.matcher(identifier.name()).matches()) { + String message = String.format("Rename this %s to match the regular expression \"%s\".", variableKind, this.format); + ctx.reportIssue(identifier, message); + } + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/WrongAssignmentOperatorCheck.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/WrongAssignmentOperatorCheck.java new file mode 100644 index 00000000..2ee338d5 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/WrongAssignmentOperatorCheck.java @@ -0,0 +1,88 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.util.List; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.api.UnaryExpressionTree.Operator; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.impl.TextRanges; + +import static java.util.Arrays.asList; +import static org.sonarsource.slang.api.AssignmentExpressionTree.Operator.EQUAL; + + +@Rule(key = "S2757") +public class WrongAssignmentOperatorCheck implements SlangCheck { + + private static final List SUSPICIOUS_UNARY_OPERATORS = asList(Operator.NEGATE, Operator.PLUS, Operator.MINUS); + + @Override + public void initialize(InitContext init) { + init.register(AssignmentExpressionTree.class, (ctx, assignment) -> { + Tree rightHandSide = assignment.statementOrExpression(); + if (assignment.operator() != EQUAL || !isSuspiciousUnaryExpression(rightHandSide)) { + return; + } + + List leftHandSideTokens = assignment.leftHandSide().metaData().tokens(); + Token variableLastToken = leftHandSideTokens.get(leftHandSideTokens.size() - 1); + + List allTokens = assignment.metaData().tokens(); + Token operatorToken = allTokens.get(allTokens.indexOf(variableLastToken) + 1); + + Token expressionFirstToken = rightHandSide.metaData().tokens().get(0); + + if (!hasSpacingBetween(operatorToken, expressionFirstToken) && hasSpacingBetween(variableLastToken, operatorToken)) { + TextRange range = TextRanges.merge(asList(operatorToken.textRange(), expressionFirstToken.textRange())); + ctx.reportIssue(range, getMessage(expressionFirstToken, ctx)); + } + }); + } + + private static String getMessage(Token expressionFirstToken, CheckContext aeTree) { + if (isSingleNegationAssignment(expressionFirstToken, aeTree)) { + // For expressions such as "a = b =! c" we want to display the other message + return "Add a space between \"=\" and \"!\" to avoid confusion."; + } + return "Was \"" + expressionFirstToken.text() + "=\" meant instead?"; + } + + private static boolean isSingleNegationAssignment(Token firstToken, CheckContext aeTree) { + return "!".equals(firstToken.text()) && !(aeTree.parent() instanceof AssignmentExpressionTree); + } + + private static boolean hasSpacingBetween(Token firstToken, Token secondToken) { + return firstToken.textRange().end().line() != secondToken.textRange().start().line() + || firstToken.textRange().end().lineOffset() != secondToken.textRange().start().lineOffset(); + } + + private static boolean isSuspiciousUnaryExpression(Tree tree) { + return tree instanceof UnaryExpressionTree && SUSPICIOUS_UNARY_OPERATORS.contains(((UnaryExpressionTree) tree).operator()); + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/complexity/CognitiveComplexity.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/complexity/CognitiveComplexity.java new file mode 100644 index 00000000..14aefbac --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/complexity/CognitiveComplexity.java @@ -0,0 +1,190 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.complexity; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.annotation.Nullable; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.CatchTree; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.utils.ExpressionUtils; +import org.sonarsource.slang.impl.JumpTreeImpl; +import org.sonarsource.slang.visitors.TreeContext; +import org.sonarsource.slang.visitors.TreeVisitor; + +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isLogicalBinaryExpression; + +public class CognitiveComplexity { + + private List increments = new ArrayList<>(); + + public CognitiveComplexity(Tree root) { + CognitiveComplexityVisitor visitor = new CognitiveComplexityVisitor(); + visitor.scan(new TreeContext(), root); + } + + public int value() { + int total = 0; + for (Increment increment : increments) { + total += increment.nestingLevel + 1; + } + return total; + } + + public List increments() { + return increments; + } + + public static class Increment { + + private final Token token; + private final int nestingLevel; + + private Increment(Token token, int nestingLevel) { + this.token = token; + this.nestingLevel = nestingLevel; + } + + public Token token() { + return token; + } + + public int nestingLevel() { + return nestingLevel; + } + } + + private class CognitiveComplexityVisitor extends TreeVisitor { + + private Set alreadyConsideredOperators = new HashSet<>(); + + private CognitiveComplexityVisitor() { + + // TODO ternary operator + // TODO "break" or "continue" with label + + register(LoopTree.class, (ctx, tree) -> incrementWithNesting(tree.keyword(), ctx)); + register(MatchTree.class, (ctx, tree) -> incrementWithNesting(tree.keyword(), ctx)); + register(CatchTree.class, (ctx, tree) -> incrementWithNesting(tree.keyword(), ctx)); + register(JumpTreeImpl.class, (ctx, tree) -> { + if (tree.label() != null) { + incrementWithoutNesting(tree.keyword()); + } + }); + + register(IfTree.class, (ctx, tree) -> { + Tree parent = ctx.ancestors().peek(); + boolean isElseIf = parent instanceof IfTree && tree == ((IfTree) parent).elseBranch(); + boolean isTernary = ExpressionUtils.isTernaryOperator(ctx.ancestors(), tree); + if (!isElseIf || isTernary) { + incrementWithNesting(tree.ifKeyword(), ctx); + } + Token elseKeyword = tree.elseKeyword(); + if (elseKeyword != null && !isTernary) { + incrementWithoutNesting(elseKeyword); + } + }); + + register(BinaryExpressionTree.class, (ctx, tree) -> handleBinaryExpressions(tree)); + } + + private void handleBinaryExpressions(BinaryExpressionTree tree) { + if (!isLogicalBinaryExpression(tree) || alreadyConsideredOperators.contains(tree.operatorToken())) { + return; + } + + List operators = new ArrayList<>(); + flattenOperators(tree, operators); + + Token previous = null; + for (Token operator : operators) { + if (previous == null || !previous.text().equals(operator.text())) { + incrementWithoutNesting(operator); + } + previous = operator; + alreadyConsideredOperators.add(operator); + } + } + + // TODO parentheses should probably be skipped + private void flattenOperators(BinaryExpressionTree tree, List operators) { + if (isLogicalBinaryExpression(tree.leftOperand())) { + flattenOperators((BinaryExpressionTree) tree.leftOperand(), operators); + } + + operators.add(tree.operatorToken()); + + if (isLogicalBinaryExpression(tree.rightOperand())) { + flattenOperators((BinaryExpressionTree) tree.rightOperand(), operators); + } + } + + private void incrementWithNesting(Token token, TreeContext ctx) { + increment(token, nestingLevel(ctx)); + } + + private void incrementWithoutNesting(Token token) { + increment(token, 0); + } + + private void increment(Token token, int nestingLevel) { + increments.add(new Increment(token, nestingLevel)); + } + + private int nestingLevel(TreeContext ctx) { + int nestingLevel = 0; + boolean isInsideFunction = false; + Iterator ancestors = ctx.ancestors().descendingIterator(); + Tree parent = null; + while (ancestors.hasNext()) { + Tree t = ancestors.next(); + if (t instanceof FunctionDeclarationTree) { + if (isInsideFunction || nestingLevel > 0) { + nestingLevel++; + } + isInsideFunction = true; + } else if ((t instanceof IfTree && !isElseIfBranch(parent, t)) || t instanceof MatchTree || t instanceof LoopTree || t instanceof CatchTree) { + nestingLevel++; + } else if (t instanceof ClassDeclarationTree) { + nestingLevel = 0; + isInsideFunction = false; + } + parent = t; + } + return nestingLevel; + } + + private boolean isElseIfBranch(@Nullable Tree parent, Tree t) { + return parent instanceof IfTree && ((IfTree) parent).elseBranch() == t; + } + + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/complexity/package-info.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/complexity/package-info.java new file mode 100644 index 00000000..c2148d2c --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/complexity/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.checks.complexity; diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/package-info.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/package-info.java new file mode 100644 index 00000000..55019120 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.checks; diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/ExpressionUtils.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/ExpressionUtils.java new file mode 100644 index 00000000..5d608515 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/ExpressionUtils.java @@ -0,0 +1,138 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +import java.util.Arrays; +import java.util.Deque; +import java.util.List; +import java.util.Optional; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.ExceptionHandlingTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.MemberSelectTree; +import org.sonarsource.slang.api.ParenthesizedExpressionTree; +import org.sonarsource.slang.api.PlaceHolderTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; + +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.CONDITIONAL_AND; +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.CONDITIONAL_OR; + +public class ExpressionUtils { + private static final String TRUE_LITERAL = "true"; + private static final String FALSE_LITERAL = "false"; + private static final List BOOLEAN_LITERALS = Arrays.asList(TRUE_LITERAL, FALSE_LITERAL); + + private ExpressionUtils() { + } + + public static boolean isBooleanLiteral(Tree tree) { + return tree instanceof LiteralTree && BOOLEAN_LITERALS.contains(((LiteralTree) tree).value()); + } + + public static boolean isFalseValueLiteral(Tree originalTree) { + Tree tree = skipParentheses(originalTree); + return (tree instanceof LiteralTree && FALSE_LITERAL.equals(((LiteralTree) tree).value())) + || (isNegation(tree) && isTrueValueLiteral(((UnaryExpressionTree) tree).operand())); + } + + public static boolean isTrueValueLiteral(Tree originalTree) { + Tree tree = skipParentheses(originalTree); + return (tree instanceof LiteralTree && TRUE_LITERAL.equals(((LiteralTree) tree).value())) + || (isNegation(tree) && isFalseValueLiteral(((UnaryExpressionTree) tree).operand())); + } + + public static boolean isNegation(Tree tree) { + return tree instanceof UnaryExpressionTree && ((UnaryExpressionTree) tree).operator() == UnaryExpressionTree.Operator.NEGATE; + } + + public static boolean isBinaryOperation(Tree tree, BinaryExpressionTree.Operator operator) { + return tree instanceof BinaryExpressionTree && ((BinaryExpressionTree) tree).operator() == operator; + } + + public static boolean isLogicalBinaryExpression(Tree tree) { + return isBinaryOperation(tree, CONDITIONAL_AND) || isBinaryOperation(tree, CONDITIONAL_OR); + } + + public static Tree skipParentheses(Tree tree) { + Tree result = tree; + while (result instanceof ParenthesizedExpressionTree) { + result = ((ParenthesizedExpressionTree) result).expression(); + } + return result; + } + + public static boolean containsPlaceHolder(Tree tree) { + return tree.descendants().anyMatch(PlaceHolderTree.class::isInstance); + } + + public static boolean isTernaryOperator(Deque ancestors, Tree tree) { + if (!isIfWithElse(tree)) { + return false; + } + Tree child = tree; + for (Tree ancestor : ancestors) { + if (ancestor instanceof BlockTree || ancestor instanceof ExceptionHandlingTree || ancestor instanceof TopLevelTree || + isBranchOfLoopOrCaseOrIfWithoutElse(ancestor, child)) { + break; + } + if (!isBranchOfIf(ancestor, child)) { + return tree.descendants().noneMatch(BlockTree.class::isInstance); + } + child = ancestor; + } + return false; + } + + private static boolean isIfWithElse(Tree tree) { + return tree instanceof IfTree && ((IfTree) tree).elseBranch() != null; + } + + private static boolean isBranchOfLoopOrCaseOrIfWithoutElse(Tree parent, Tree child) { + return (parent instanceof LoopTree && child == ((LoopTree) parent).body()) || + (parent instanceof MatchCaseTree && child == ((MatchCaseTree) parent).body()) || + (isBranchOfIf(parent, child) && ((IfTree) parent).elseBranch() == null); + } + + private static boolean isBranchOfIf(Tree parent, Tree child) { + if (parent instanceof IfTree) { + IfTree ifTree = (IfTree) parent; + return child == ifTree.thenBranch() || child == ifTree.elseBranch(); + } + return false; + } + + public static Optional getMemberSelectOrIdentifierName(Tree tree) { + if (tree instanceof IdentifierTree) { + return Optional.of(((IdentifierTree) tree).name()); + } else if (tree instanceof MemberSelectTree) { + return Optional.of(((MemberSelectTree)tree).identifier().name()); + } else { + return Optional.empty(); + } + } + +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/FunctionUtils.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/FunctionUtils.java new file mode 100644 index 00000000..4dd39dc1 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/FunctionUtils.java @@ -0,0 +1,99 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.FunctionInvocationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.MemberSelectTree; +import org.sonarsource.slang.api.ModifierTree; +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.api.Tree; + +import static org.sonarsource.slang.api.ModifierTree.Kind.OVERRIDE; +import static org.sonarsource.slang.api.ModifierTree.Kind.PRIVATE; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.getMemberSelectOrIdentifierName; + +public class FunctionUtils { + + private FunctionUtils() { + } + + public static boolean isPrivateMethod(FunctionDeclarationTree method) { + return hasModifierMethod(method, PRIVATE); + + } + + public static boolean isOverrideMethod(FunctionDeclarationTree method) { + return hasModifierMethod(method, OVERRIDE); + } + + public static boolean hasModifierMethod(FunctionDeclarationTree method, ModifierTree.Kind kind) { + return method.modifiers().stream() + .filter(ModifierTree.class::isInstance) + .map(ModifierTree.class::cast) + .anyMatch(modifier -> modifier.kind() == kind); + } + + public static boolean hasFunctionCallNameIgnoreCase(FunctionInvocationTree tree, String name) { + return getFunctionInvocationName(tree).filter(name::equalsIgnoreCase).isPresent(); + } + + public static Set getStringsTokens(FunctionDeclarationTree functionDeclarationTree, String delimitersRegex) { + Set stringLiteralTokens = new HashSet<>(); + functionDeclarationTree.descendants() + .filter(StringLiteralTree.class::isInstance) + .map(StringLiteralTree.class::cast) + .map(StringLiteralTree::content) + .forEach(literal -> stringLiteralTokens.addAll(Arrays.asList(literal.split(delimitersRegex)))); + return stringLiteralTokens; + } + + private static Optional getFunctionInvocationName(FunctionInvocationTree tree) { + return getMemberSelectOrIdentifierName(tree.memberSelect()); + } + + public static boolean hasFunctionCallFullNameIgnoreCase(FunctionInvocationTree tree, String... names) { + return hasFunctionCallFullNameIgnoreCaseHelper(tree.memberSelect(), Arrays.asList(names)); + } + + private static boolean hasFunctionCallFullNameIgnoreCaseHelper(Tree tree, List names) { + if (tree instanceof IdentifierTree) { + return names.size() == 1 && ((IdentifierTree) tree).name().equalsIgnoreCase(names.get(0)); + } else if (tree instanceof MemberSelectTree) { + MemberSelectTree memberSelectTree = (MemberSelectTree) tree; + return names.size() > 1 + && memberSelectTree.identifier().name().equalsIgnoreCase(names.get(names.size()-1)) + && hasFunctionCallFullNameIgnoreCaseHelper(memberSelectTree.expression(), dropLastElement(names)); + } else { + // Any other node (native, ...): we don't known anything about them! + return false; + } + } + + private static List dropLastElement(List list) { + return list.subList(0, list.size()-1); + } +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/Language.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/Language.java new file mode 100644 index 00000000..4a5b3f0d --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/Language.java @@ -0,0 +1,44 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +/** + * This enum is used only to distinguish default values for rule parameters. This should be the sole exception in otherwise + * language agnostic module + */ +public enum Language { + RUBY, SCALA, GO; + + public static final String RUBY_NAMING_DEFAULT = "^(@{0,2}[\\da-z_]+[!?=]?)|([*+-/%=!><~]+)|(\\[]=?)$"; + + // scala constant starts with upper-case + public static final String SCALA_NAMING_DEFAULT = "^[_a-zA-Z][a-zA-Z0-9]*$"; + + // support function name suffix '_=', '_+', '_!', ... and operators '+', '-', ... + public static final String SCALA_FUNCTION_OR_OPERATOR_NAMING_DEFAULT = "^([a-z][a-zA-Z0-9]*+(_[^a-zA-Z0-9]++)?+|[^a-zA-Z0-9]++)$"; + + public static final String GO_NAMING_DEFAULT = "^(_|[a-zA-Z0-9]+)$"; + + public static final int GO_NESTED_STATEMENT_MAX_DEPTH = 4; + public static final int GO_MATCH_CASES_DEFAULT_MAX = 6; + public static final int GO_DEFAULT_MAXIMUM_LINE_LENGTH = 120; + public static final int GO_DEFAULT_FILE_LINE_MAX = 750; + public static final int GO_DEFAULT_MAXIMUM_FUNCTION_LENGTH = 120; +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/PropertyDefaultValue.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/PropertyDefaultValue.java new file mode 100644 index 00000000..9b6d6eb4 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/PropertyDefaultValue.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +@Repeatable(PropertyDefaultValues.class) +public @interface PropertyDefaultValue { + + Language language(); + + String defaultValue(); +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/PropertyDefaultValues.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/PropertyDefaultValues.java new file mode 100644 index 00000000..44a19672 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/PropertyDefaultValues.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.FIELD}) +public @interface PropertyDefaultValues { + PropertyDefaultValue[] value(); +} diff --git a/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/package-info.java b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/package-info.java new file mode 100644 index 00000000..445b08f1 --- /dev/null +++ b/slang-checks/src/main/java/org/sonarsource/slang/checks/utils/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.checks.utils; diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/AllBranchesIdenticalCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/AllBranchesIdenticalCheckTest.java new file mode 100644 index 00000000..2c07d756 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/AllBranchesIdenticalCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class AllBranchesIdenticalCheckTest { + + @Test + void test() { Verifier.verify("AllBranchesIdentical.slang", new AllBranchesIdenticalCheck()); } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/BadClassNameCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/BadClassNameCheckTest.java new file mode 100644 index 00000000..54f4f60c --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/BadClassNameCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class BadClassNameCheckTest { + + @Test + void test() { + Verifier.verify("BadClassName.slang", new BadClassNameCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/BadFunctionNameCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/BadFunctionNameCheckTest.java new file mode 100644 index 00000000..b45ff252 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/BadFunctionNameCheckTest.java @@ -0,0 +1,37 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class BadFunctionNameCheckTest { + + @Test + void test() { + Verifier.verify("BadFunctionName.slang", new BadFunctionNameCheck()); + } + + @Test + void test_upper_case() { + BadFunctionNameCheck check = new BadFunctionNameCheck(); + check.format = "^[A-Z]*$"; + Verifier.verify("BadFunctionName.uppercase.slang", check); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/BooleanInversionCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/BooleanInversionCheckTest.java new file mode 100644 index 00000000..e5924142 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/BooleanInversionCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class BooleanInversionCheckTest { + + @Test + void test() { + Verifier.verify("BooleanInversion.slang", new BooleanInversionCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/BooleanLiteralCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/BooleanLiteralCheckTest.java new file mode 100644 index 00000000..b0eba036 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/BooleanLiteralCheckTest.java @@ -0,0 +1,27 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class BooleanLiteralCheckTest { + @Test + void test() { Verifier.verify("BooleanLiteral.slang", new BooleanLiteralCheck()); } +} \ No newline at end of file diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/CheckListTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/CheckListTest.java new file mode 100644 index 00000000..8a952d97 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/CheckListTest.java @@ -0,0 +1,61 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.io.File; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.checks.CheckList.ALL_CHECKS_WITH_LANGUAGE_CONFIG; + +class CheckListTest { + + @Test + void all_checks_should_be_present() { + File directory = new File("src/main/java/org/sonarsource/slang/checks"); + File[] checkFiles = directory.listFiles((dir, name) -> + name.endsWith("Check.java") && !name.startsWith("Abstract")); + assertThat(CheckList.allChecks().size() + ALL_CHECKS_WITH_LANGUAGE_CONFIG.length).isEqualTo(checkFiles.length); + } + + @Test + void exclude_checks() { + List> allChecks = CheckList.allChecks(); + assertThat(allChecks).hasSizeGreaterThanOrEqualTo(40); + + List> includedChecks = CheckList.excludeChecks(new Class[] {AllBranchesIdenticalCheck.class}); + assertThat(includedChecks).hasSize(allChecks.size() - 1); + } + + @Test + void configured_checks() { + List> checksWithConfig = Arrays.asList(ALL_CHECKS_WITH_LANGUAGE_CONFIG); + assertThat(checksWithConfig).hasSize(1); + + Set> allChecks = new HashSet<>(CheckList.allChecks()); + allChecks.removeAll(checksWithConfig); + assertThat(allChecks).hasSize(CheckList.allChecks().size()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/CodeAfterJumpCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/CodeAfterJumpCheckTest.java new file mode 100644 index 00000000..89888a29 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/CodeAfterJumpCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class CodeAfterJumpCheckTest { + + @Test + void test() { + Verifier.verify("CodeAfterJump.slang", new CodeAfterJumpCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/CollapsibleIfStatementsCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/CollapsibleIfStatementsCheckTest.java new file mode 100644 index 00000000..71df53e0 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/CollapsibleIfStatementsCheckTest.java @@ -0,0 +1,27 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class CollapsibleIfStatementsCheckTest { + @Test + void test() { Verifier.verify("CollapsibleIfStatements.slang", new CollapsibleIfStatementsCheck()); } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/CommentedCodeCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/CommentedCodeCheckTest.java new file mode 100644 index 00000000..5c5942be --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/CommentedCodeCheckTest.java @@ -0,0 +1,32 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.parser.SlangCodeVerifier; + +class CommentedCodeCheckTest { + + @Test + void test() { + Verifier.verify("CommentedCode.slang", new CommentedCodeCheck(new SlangCodeVerifier())); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/DuplicateBranchCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/DuplicateBranchCheckTest.java new file mode 100644 index 00000000..1bcf4181 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/DuplicateBranchCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class DuplicateBranchCheckTest { + + @Test + void test() { Verifier.verify("DuplicateBranch.slang", new DuplicateBranchCheck()); } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/DuplicatedFunctionImplementationCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/DuplicatedFunctionImplementationCheckTest.java new file mode 100644 index 00000000..e29bd802 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/DuplicatedFunctionImplementationCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class DuplicatedFunctionImplementationCheckTest { + + @Test + void test() { + Verifier.verify("DuplicatedFunctionImplementation.slang", new DuplicatedFunctionImplementationCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/ElseIfWithoutElseCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/ElseIfWithoutElseCheckTest.java new file mode 100644 index 00000000..ffb3d4b7 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/ElseIfWithoutElseCheckTest.java @@ -0,0 +1,27 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class ElseIfWithoutElseCheckTest { + @Test + void test() { Verifier.verify("ElseIfWithoutElse.slang", new ElseIfWithoutElseCheck()); } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyBlockCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyBlockCheckTest.java new file mode 100644 index 00000000..d82a12ec --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyBlockCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class EmptyBlockCheckTest { + + @Test + void test() { Verifier.verify("EmptyBlock.slang", new EmptyBlockCheck()); } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyCommentCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyCommentCheckTest.java new file mode 100644 index 00000000..61409edd --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyCommentCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class EmptyCommentCheckTest { + + @Test + void test() { Verifier.verify("EmptyComment.slang", new EmptyCommentCheck()); } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyFunctionCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyFunctionCheckTest.java new file mode 100644 index 00000000..3495f95b --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/EmptyFunctionCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class EmptyFunctionCheckTest { + + @Test + void test() { Verifier.verify("EmptyFunction.slang", new EmptyFunctionCheck()); } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/FileHeaderCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/FileHeaderCheckTest.java new file mode 100644 index 00000000..628e935c --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/FileHeaderCheckTest.java @@ -0,0 +1,56 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class FileHeaderCheckTest { + private FileHeaderCheck check = new FileHeaderCheck(); + @Test + void test() { + check.headerFormat = "// copyright 2018"; + Verifier.verify("fileheader/Noncompliant.slang", check); + Verifier.verifyNoIssue("fileheader/Compliant.slang", check); + } + + @Test + void test_regex() { + check.headerFormat = "// copyright 20\\d\\d"; + check.isRegularExpression = true; + Verifier.verify("fileheader/Noncompliant.slang", check); + Verifier.verifyNoIssue("fileheader/Compliant.slang", check); + } + @Test + void test_multiline() { + check.headerFormat = "/*\n" + + " * SonarSource SLang\n" + + " * Copyright (C) 1999-2001 SonarSource SA\n" + + " * mailto:info AT sonarsource DOT com\n" + + " */"; + Verifier.verifyNoIssue("fileheader/Multiline.slang", check); + } + + @Test + void test_no_first_line() { + check.headerFormat = "// copyright 20\\d\\d"; + check.isRegularExpression = true; + Verifier.verify("fileheader/NoFirstLine.slang", check); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/FixMeCommentCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/FixMeCommentCheckTest.java new file mode 100644 index 00000000..f84449b9 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/FixMeCommentCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class FixMeCommentCheckTest { + + @Test + void test() { + Verifier.verify("FixMeComment.slang", new FixMeCommentCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/FunctionCognitiveComplexityCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/FunctionCognitiveComplexityCheckTest.java new file mode 100644 index 00000000..82cfe1e0 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/FunctionCognitiveComplexityCheckTest.java @@ -0,0 +1,33 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class FunctionCognitiveComplexityCheckTest { + + @Test + void test() { + FunctionCognitiveComplexityCheck check = new FunctionCognitiveComplexityCheck(); + check.threshold = 4; + Verifier.verify("FunctionCognitiveComplexity.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/HardcodedCredentialsCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/HardcodedCredentialsCheckTest.java new file mode 100644 index 00000000..086d9f0b --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/HardcodedCredentialsCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class HardcodedCredentialsCheckTest { + + @Test + void test() { + Verifier.verify("HardcodedCredentials.slang", new HardcodedCredentialsCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/HardcodedIpCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/HardcodedIpCheckTest.java new file mode 100644 index 00000000..9cb063fa --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/HardcodedIpCheckTest.java @@ -0,0 +1,30 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class HardcodedIpCheckTest { + @Test + void test() { + Verifier.verify("HardcodedIp.slang", new HardcodedIpCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/IdenticalBinaryOperandCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/IdenticalBinaryOperandCheckTest.java new file mode 100644 index 00000000..63d6c1f8 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/IdenticalBinaryOperandCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class IdenticalBinaryOperandCheckTest { + + @Test + void test() { + Verifier.verify("IdenticalBinaryOperand.slang", new IdenticalBinaryOperandCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/IdenticalConditionsCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/IdenticalConditionsCheckTest.java new file mode 100644 index 00000000..953d7611 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/IdenticalConditionsCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class IdenticalConditionsCheckTest { + + @Test + void test() { + Verifier.verify("IdenticalConditions.slang", new IdenticalConditionsCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalseCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalseCheckTest.java new file mode 100644 index 00000000..ffa68dd5 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalseCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class IfConditionalAlwaysTrueOrFalseCheckTest { + + @Test + void test() { + Verifier.verify("IfConditionalAlwaysTrueOrFalse.slang", new IfConditionalAlwaysTrueOrFalseCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/MatchCaseTooBigCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/MatchCaseTooBigCheckTest.java new file mode 100644 index 00000000..7e62c9a6 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/MatchCaseTooBigCheckTest.java @@ -0,0 +1,39 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class MatchCaseTooBigCheckTest { + + private MatchCaseTooBigCheck check = new MatchCaseTooBigCheck(); + + @Test + void max_5() { + check.max = 5; + Verifier.verify("MatchCaseTooBig_5.slang", check); + } + + @Test + void max_3() { + check.max = 3; + Verifier.verify("MatchCaseTooBig_3.slang", check); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/MatchWithoutElseCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/MatchWithoutElseCheckTest.java new file mode 100644 index 00000000..54ce101d --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/MatchWithoutElseCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class MatchWithoutElseCheckTest { + + @Test + void test() { + Verifier.verify("MatchWithoutElse.slang", new MatchWithoutElseCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/NestedMatchCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/NestedMatchCheckTest.java new file mode 100644 index 00000000..91a89ce4 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/NestedMatchCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class NestedMatchCheckTest { + @Test + void test() { + Verifier.verify("NestedMatch.slang", new NestedMatchCheck()); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/OctalValuesCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/OctalValuesCheckTest.java new file mode 100644 index 00000000..0a8721dc --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/OctalValuesCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class OctalValuesCheckTest { + + @Test + void test() { + Verifier.verify("OctalValues.slang", new OctalValuesCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/OneStatementPerLineCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/OneStatementPerLineCheckTest.java new file mode 100644 index 00000000..51c6fbfb --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/OneStatementPerLineCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class OneStatementPerLineCheckTest { + @Test + void test() { + Verifier.verify("OneStatementPerLine.slang", new OneStatementPerLineCheck()); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/RedundantParenthesesCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/RedundantParenthesesCheckTest.java new file mode 100644 index 00000000..ae2fa6ac --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/RedundantParenthesesCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class RedundantParenthesesCheckTest { + + @Test + void test() { + Verifier.verify("RedundantParentheses.slang", new RedundantParenthesesCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/SelfAssignmentCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/SelfAssignmentCheckTest.java new file mode 100644 index 00000000..e43f937a --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/SelfAssignmentCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class SelfAssignmentCheckTest { + + @Test + void test() { + Verifier.verify("SelfAssignment.slang", new SelfAssignmentCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/StringLiteralDuplicatedCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/StringLiteralDuplicatedCheckTest.java new file mode 100644 index 00000000..8282bcd1 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/StringLiteralDuplicatedCheckTest.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class StringLiteralDuplicatedCheckTest { + + @Test + void test() { + Verifier.verify("StringLiteralDuplicated.slang", new StringLiteralDuplicatedCheck()); + } + + @Test + void test_threshold_4() { + StringLiteralDuplicatedCheck check = new StringLiteralDuplicatedCheck(); + check.threshold = 4; + Verifier.verify("StringLiteralDuplicated.threshold_4.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TabsCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TabsCheckTest.java new file mode 100644 index 00000000..18e6967f --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TabsCheckTest.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TabsCheckTest { + + @Test + void test_without_tabs() { + Verifier.verifyNoIssue("Tabs_compliant.slang", new TabsCheck()); + } + + @Test + void test_with_tabs() { + Verifier.verify("Tabs_noncompliant.slang", new TabsCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TodoCommentCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TodoCommentCheckTest.java new file mode 100644 index 00000000..a8019e18 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TodoCommentCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TodoCommentCheckTest { + + @Test + void test() { + Verifier.verify("TodoComment.slang", new TodoCommentCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TooComplexExpressionCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooComplexExpressionCheckTest.java new file mode 100644 index 00000000..67f4550c --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooComplexExpressionCheckTest.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TooComplexExpressionCheckTest { + + @Test + void test_max_3() { + Verifier.verify("TooComplexExpression_3.slang", new TooComplexExpressionCheck()); + } + + @Test + void test_max_2() { + TooComplexExpressionCheck check = new TooComplexExpressionCheck(); + check.max = 2; + Verifier.verify("TooComplexExpression_2.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TooDeeplyNestedStatementsCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooDeeplyNestedStatementsCheckTest.java new file mode 100644 index 00000000..c01281be --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooDeeplyNestedStatementsCheckTest.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TooDeeplyNestedStatementsCheckTest { + + @Test + void test() { + Verifier.verify("TooDeeplyNestedStatements.slang", new TooDeeplyNestedStatementsCheck()); + } + + @Test + void test_max_2() { + TooDeeplyNestedStatementsCheck check = new TooDeeplyNestedStatementsCheck(); + check.max = 2; + Verifier.verify("TooDeeplyNestedStatements.max_2.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TooLongFunctionCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooLongFunctionCheckTest.java new file mode 100644 index 00000000..17aa289b --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooLongFunctionCheckTest.java @@ -0,0 +1,40 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TooLongFunctionCheckTest { + + TooLongFunctionCheck check = new TooLongFunctionCheck(); + + @Test + void max_3() { + check.max = 3; + Verifier.verify("TooLongFunction_3.slang", check); + } + + @Test + void max_4() { + check.max = 4; + Verifier.verify("TooLongFunction_4.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TooLongLineCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooLongLineCheckTest.java new file mode 100644 index 00000000..b15a4916 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooLongLineCheckTest.java @@ -0,0 +1,39 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TooLongLineCheckTest { + + private TooLongLineCheck check = new TooLongLineCheck(); + + @Test + void max_120() { + check.maximumLineLength = 120; + Verifier.verify("TooLongLine_120.slang", check); + } + + @Test + void max_40() { + check.maximumLineLength = 40; + Verifier.verify("TooLongLine_40.slang", check); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyCasesCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyCasesCheckTest.java new file mode 100644 index 00000000..894ec2d8 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyCasesCheckTest.java @@ -0,0 +1,40 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TooManyCasesCheckTest { + + private TooManyCasesCheck check = new TooManyCasesCheck(); + + @Test + void test_3() { + check.maximum = 3; + Verifier.verify("TooManyCases_3.slang", check); + } + + @Test + void test_4() { + check.maximum = 4; + Verifier.verify("TooManyCases_4.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyLinesOfCodeFileCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyLinesOfCodeFileCheckTest.java new file mode 100644 index 00000000..b1c888b8 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyLinesOfCodeFileCheckTest.java @@ -0,0 +1,40 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TooManyLinesOfCodeFileCheckTest { + + TooManyLinesOfCodeFileCheck check = new TooManyLinesOfCodeFileCheck(); + + @Test + void max_4() { + check.max = 4; + Verifier.verify("TooManyLinesOfCodeFile.max_4.slang", check); + } + + @Test + void max_5() { + check.max = 5; + Verifier.verifyNoIssue("TooManyLinesOfCodeFile.max_5.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyParametersCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyParametersCheckTest.java new file mode 100644 index 00000000..f17fe4da --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/TooManyParametersCheckTest.java @@ -0,0 +1,38 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class TooManyParametersCheckTest { + + @Test + void default_threshold() { + Verifier.verify("TooManyParameters.slang", new TooManyParametersCheck()); + } + + @Test + void threshold_3() { + TooManyParametersCheck check = new TooManyParametersCheck(); + check.max = 3; + Verifier.verify("TooManyParameters.threshold.3.slang", check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedFunctionParameterCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedFunctionParameterCheckTest.java new file mode 100644 index 00000000..58c28011 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedFunctionParameterCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class UnusedFunctionParameterCheckTest { + + @Test + void test() { + Verifier.verify("UnusedFunctionParameter.slang", new UnusedFunctionParameterCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedLocalVariableCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedLocalVariableCheckTest.java new file mode 100644 index 00000000..2a7f7bff --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedLocalVariableCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class UnusedLocalVariableCheckTest { + + @Test + void test() { + Verifier.verify("UnusedLocalVariable.slang", new UnusedLocalVariableCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedPrivateMethodCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedPrivateMethodCheckTest.java new file mode 100644 index 00000000..39d7c81d --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/UnusedPrivateMethodCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class UnusedPrivateMethodCheckTest { + + @Test + void test() { + Verifier.verify("UnusedPrivateMethod.slang", new UnusedPrivateMethodCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/VariableAndParameterNameCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/VariableAndParameterNameCheckTest.java new file mode 100644 index 00000000..265aa501 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/VariableAndParameterNameCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class VariableAndParameterNameCheckTest { + + @Test + void test() { + Verifier.verify("VariableAndParameterName.slang", new VariableAndParameterNameCheck()); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/Verifier.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/Verifier.java new file mode 100644 index 00000000..ed366415 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/Verifier.java @@ -0,0 +1,41 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import java.nio.file.Path; +import java.nio.file.Paths; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.parser.SLangConverter; + +public class Verifier { + + private static final Path BASE_DIR = Paths.get("src", "test", "resources", "org", "sonarsource", "slang", "checks"); + private static final ASTConverter CONVERTER = new SLangConverter(); + + public static void verify(String fileName, SlangCheck check) { + org.sonarsource.slang.testing.Verifier.verify(CONVERTER, BASE_DIR.resolve(fileName), check); + } + + public static void verifyNoIssue(String fileName, SlangCheck check) { + org.sonarsource.slang.testing.Verifier.verifyNoIssue(CONVERTER, BASE_DIR.resolve(fileName), check); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/WrongAssignmentOperatorCheckTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/WrongAssignmentOperatorCheckTest.java new file mode 100644 index 00000000..1b238c53 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/WrongAssignmentOperatorCheckTest.java @@ -0,0 +1,31 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks; + +import org.junit.jupiter.api.Test; + +class WrongAssignmentOperatorCheckTest { + + @Test + void test() { + Verifier.verify("WrongAssignmentOperator.slang", new WrongAssignmentOperatorCheck()); + } + +} \ No newline at end of file diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/complexity/CognitiveComplexityTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/complexity/CognitiveComplexityTest.java new file mode 100644 index 00000000..85c4a89e --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/complexity/CognitiveComplexityTest.java @@ -0,0 +1,143 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.complexity; + +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.parser.SLangConverter; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CognitiveComplexityTest { + + private SLangConverter parser = new SLangConverter(); + + @Test + void unrelated_statement() { + assertThat(complexity("42;").value()).isZero(); + } + + @Test + void if_statements() { + assertThat(complexity("if (x) { 42 };").value()).isEqualTo(1); + assertThat(complexity("if (x) { 42 } else { 43 };").value()).isEqualTo(2); + assertThat(complexity("if (x) { 42 } else if (y) { 43 };").value()).isEqualTo(2); + assertThat(complexity("if (x) { 42 } else if (y) { 43 } else { 44 };").value()).isEqualTo(3); + } + + @Test + void ternary_operator() { + assertThat(complexity("v = if (x) 42 else 43;").value()).isEqualTo(1); + assertThat(complexity("v = if (x) 42 else if (y) 43 else 44;").value()).isEqualTo(3); + assertThat(complexity("v = if (x) (if (y) 42 else 43) else 44;").value()).isEqualTo(3); + assertThat(complexity("v = if (x) 42 else (if (y) 43 else 44);").value()).isEqualTo(3); + assertThat(complexity("v = if (x) (if (y) 42 else 43) else (if (y) 44 else 45);").value()).isEqualTo(5); + assertThat(complexity("v = if (x) if (y) if (z) 42 else 43 else 44 else 45;").value()).isEqualTo(6); + assertThat(complexity("v = if (x) (if (y) (if (z) 42 else 43) else 44) else 45;").value()).isEqualTo(6); + } + + @Test + void nested_if_statements() { + assertThat(complexity("if (x) { 42 };").value()).isEqualTo(1); + assertThat(complexity("if (x) { 42 } else { 43 };").value()).isEqualTo(2); + assertThat(complexity("if (x) { 42 } else if (y) { 43 };").value()).isEqualTo(2); + assertThat(complexity("if (x) { 42 } else if (y) { if (y) { 43 } else { 44 } };").value()).isEqualTo(5); + assertThat(complexity("if (x) { 42 } else if (y) { 43 } else { 44 };").value()).isEqualTo(3); + assertThat(complexity("if (x) { 42 } else if (y) { 43 } else { if (y) { 44 } else { 45 } };").value()).isEqualTo(6); + + } + + @Test + void loop_statements() { + assertThat(complexity("while (x) { 42 };").value()).isEqualTo(1); + } + + @Test + void match_statements() { + assertThat(complexity("match (x) { else -> 42; };").value()).isEqualTo(1); + assertThat(complexity("match (x) { 'a' -> 0; else -> 42; };").value()).isEqualTo(1); + } + + @Test + void try_catch_statements() { + assertThat(complexity("try { foo; };").value()).isZero(); + assertThat(complexity("try { foo; } catch (e1) { bar; };").value()).isEqualTo(1); + assertThat(complexity("try { foo; } catch (e1) { bar; } catch (e2) { baz; };").value()).isEqualTo(2); + assertThat(complexity("try { foo; } finally { bar; };").value()).isZero(); + } + + @Test + void functions() { + assertThat(complexity("fun foo() { 42 }").value()).isZero(); + assertThat(complexity("fun foo() { f = fun() { 42 }; }").value()).isZero(); + } + + @Test + void binary_operators() { + assertThat(complexity("a == b;").value()).isZero(); + assertThat(complexity("a && b;").value()).isEqualTo(1); + assertThat(complexity("a || b;").value()).isEqualTo(1); + assertThat(complexity("a && b && c;").value()).isEqualTo(1); + assertThat(complexity("a || b || c;").value()).isEqualTo(1); + assertThat(complexity("a || b && c;").value()).isEqualTo(2); + assertThat(complexity("a || b && c || d;").value()).isEqualTo(3); + } + + @Test + void jumps() { + assertThat(complexity("break;").value()).isZero(); + assertThat(complexity("break foo;").value()).isEqualTo(1); + assertThat(complexity("while (x) break;").value()).isEqualTo(1); + assertThat(complexity("while (x) break foo;").value()).isEqualTo(2); + + assertThat(complexity("continue;").value()).isZero(); + assertThat(complexity("continue foo;").value()).isEqualTo(1); + assertThat(complexity("while (x) continue;").value()).isEqualTo(1); + assertThat(complexity("while (x) continue foo;").value()).isEqualTo(2); + } + + @Test + void nesting() { + assertThat(complexity("if (x) a && b;").value()).isEqualTo(2); + assertThat(complexity("if (x) if (y) 42;").value()).isEqualTo(3); + assertThat(complexity("while (x) if (y) 42;").value()).isEqualTo(3); + assertThat(complexity("match (x) { else -> if (y) 42; };").value()).isEqualTo(3); + assertThat(complexity("try { x } catch (e) { if (y) 42; };").value()).isEqualTo(3); + assertThat(complexity("try { if (y) 42; } catch (e) { x };").value()).isEqualTo(2); + assertThat(complexity("fun foo() { if (x) 42; }").value()).isEqualTo(1); + assertThat(complexity("fun foo() { f = fun() { if (x) 42; }; }").value()).isEqualTo(2); + assertThat(complexity("if (x) { f = fun() { if (x) 42; }; };").value()).isEqualTo(4); + } + + @Test + void nesting_with_classes() { + assertThat(complexity("class A { if (x) a && b; }").value()).isEqualTo(2); + assertThat(complexity("class A { fun foo() { if (x) a && b; } }").value()).isEqualTo(2); + assertThat(complexity("class A { fun foo() { fun bar() { if (x) a && b; } } }").value()).isEqualTo(3); + assertThat(complexity("class A { fun foo() { class B { fun bar() { if (x) a && b; } } } }").value()).isEqualTo(2); + assertThat(complexity("class A { fun foo() { if (x) a && b; class B { fun bar() { if (x) a && b; } } } }").value()).isEqualTo(4); + } + + + private CognitiveComplexity complexity(String code) { + Tree tree = parser.parse(code); + return new CognitiveComplexity(tree); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/ExpressionUtilsTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/ExpressionUtilsTest.java new file mode 100644 index 00000000..47e1d127 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/ExpressionUtilsTest.java @@ -0,0 +1,112 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.ParenthesizedExpressionTreeImpl; +import org.sonarsource.slang.impl.UnaryExpressionTreeImpl; +import org.junit.jupiter.api.Test; + +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.CONDITIONAL_AND; +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.CONDITIONAL_OR; +import static org.sonarsource.slang.api.BinaryExpressionTree.Operator.EQUAL_TO; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isBinaryOperation; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isBooleanLiteral; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isFalseValueLiteral; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isLogicalBinaryExpression; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isNegation; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.isTrueValueLiteral; +import static org.sonarsource.slang.checks.utils.ExpressionUtils.skipParentheses; +import static org.assertj.core.api.Assertions.assertThat; + +class ExpressionUtilsTest { + private static Tree TRUE_LITERAL = new LiteralTreeImpl(null, "true"); + private static Tree FALSE_LITERAL = new LiteralTreeImpl(null, "false"); + private static Tree NUMBER_LITERAL = new LiteralTreeImpl(null, "34"); + private static Tree TRUE_NEGATED = new UnaryExpressionTreeImpl(null, UnaryExpressionTree.Operator.NEGATE, TRUE_LITERAL); + private static Tree FALSE_NEGATED = new UnaryExpressionTreeImpl(null, UnaryExpressionTree.Operator.NEGATE, FALSE_LITERAL); + + @Test + void test_boolean_literal() { + assertThat(isBooleanLiteral(TRUE_LITERAL)).isTrue(); + assertThat(isBooleanLiteral(FALSE_LITERAL)).isTrue(); + assertThat(isBooleanLiteral(NUMBER_LITERAL)).isFalse(); + assertThat(isBooleanLiteral(TRUE_NEGATED)).isFalse(); + } + + @Test + void test_false_literal_value() { + assertThat(isFalseValueLiteral(TRUE_LITERAL)).isFalse(); + assertThat(isFalseValueLiteral(FALSE_LITERAL)).isTrue(); + assertThat(isFalseValueLiteral(NUMBER_LITERAL)).isFalse(); + assertThat(isFalseValueLiteral(TRUE_NEGATED)).isTrue(); + assertThat(isFalseValueLiteral(FALSE_NEGATED)).isFalse(); + } + + @Test + void test_true_literal_value() { + assertThat(isTrueValueLiteral(TRUE_LITERAL)).isTrue(); + assertThat(isTrueValueLiteral(FALSE_LITERAL)).isFalse(); + assertThat(isTrueValueLiteral(NUMBER_LITERAL)).isFalse(); + assertThat(isTrueValueLiteral(TRUE_NEGATED)).isFalse(); + assertThat(isTrueValueLiteral(FALSE_NEGATED)).isTrue(); + } + + @Test + void test_negation() { + assertThat(isNegation(FALSE_LITERAL)).isFalse(); + assertThat(isNegation(NUMBER_LITERAL)).isFalse(); + assertThat(isNegation(TRUE_NEGATED)).isTrue(); + } + + @Test + void test_binary_operation() { + Tree binaryAnd = new BinaryExpressionTreeImpl(null, CONDITIONAL_AND, null, TRUE_LITERAL, FALSE_LITERAL); + + assertThat(isBinaryOperation(binaryAnd, CONDITIONAL_AND)).isTrue(); + assertThat(isBinaryOperation(binaryAnd, CONDITIONAL_OR)).isFalse(); + } + + @Test + void test_logical_binary_operation() { + Tree binaryAnd = new BinaryExpressionTreeImpl(null, CONDITIONAL_AND, null, TRUE_LITERAL, FALSE_LITERAL); + Tree binaryOr = new BinaryExpressionTreeImpl(null, CONDITIONAL_OR, null, TRUE_LITERAL, FALSE_LITERAL); + Tree binaryEqual = new BinaryExpressionTreeImpl(null, EQUAL_TO, null, TRUE_LITERAL, FALSE_LITERAL); + + assertThat(isLogicalBinaryExpression(binaryAnd)).isTrue(); + assertThat(isLogicalBinaryExpression(binaryOr)).isTrue(); + assertThat(isLogicalBinaryExpression(binaryEqual)).isFalse(); + assertThat(isLogicalBinaryExpression(TRUE_NEGATED)).isFalse(); + } + + @Test + void test_skip_parentheses() { + Tree parenthesizedExpression1 = new ParenthesizedExpressionTreeImpl(null, TRUE_LITERAL, null, null); + Tree parenthesizedExpression2 = new ParenthesizedExpressionTreeImpl(null, parenthesizedExpression1, null, null); + + assertThat(skipParentheses(parenthesizedExpression1)).isEqualTo(TRUE_LITERAL); + assertThat(skipParentheses(parenthesizedExpression2)).isEqualTo(TRUE_LITERAL); + assertThat(skipParentheses(TRUE_LITERAL)).isEqualTo(TRUE_LITERAL); + } + +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/FunctionUtilsTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/FunctionUtilsTest.java new file mode 100644 index 00000000..2eae879d --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/FunctionUtilsTest.java @@ -0,0 +1,124 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.FunctionInvocationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.FunctionInvocationTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.MemberSelectTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.parser.SLangConverter; + +import static org.sonarsource.slang.checks.utils.FunctionUtils.hasFunctionCallFullNameIgnoreCase; +import static org.sonarsource.slang.checks.utils.FunctionUtils.hasFunctionCallNameIgnoreCase; +import static org.assertj.core.api.Assertions.assertThat; + +class FunctionUtilsTest { + private class TypeNativeKind implements NativeKind {} + + private static TreeMetaData meta = null; + private static IdentifierTree identifierTree = new IdentifierTreeImpl(meta, "function"); + private static List args = new ArrayList<>(); + + private static final ASTConverter CONVERTER = new SLangConverter(); + + @Test + void test_has_function_name_identifier() { + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, identifierTree, args); + assertThat(hasFunctionCallNameIgnoreCase(tree, "function")).isTrue(); + assertThat(hasFunctionCallNameIgnoreCase(tree, "FuNcTiOn")).isTrue(); + assertThat(hasFunctionCallNameIgnoreCase(tree, "mySuperFunction")).isFalse(); + } + + @Test + void test_has_function_name_method_select() { + Tree member = new IdentifierTreeImpl(meta, "A"); + Tree methodSelect = new MemberSelectTreeImpl(meta, member, identifierTree); + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, methodSelect, args); + assertThat(hasFunctionCallNameIgnoreCase(tree, "function")).isTrue(); + assertThat(hasFunctionCallNameIgnoreCase(tree, "A")).isFalse(); + } + + @Test + void test_has_function_name_unknown() { + Tree nativeNode = new NativeTreeImpl(meta, new TypeNativeKind(), null); + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, nativeNode, args); + assertThat(hasFunctionCallNameIgnoreCase(tree, "function")).isFalse(); + } + + @Test + void test_has_function_full_name_identifier() { + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, identifierTree, args); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "function")).isTrue(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "FuNcTioN")).isTrue(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "mySuperFunction")).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree)).isFalse(); + } + + @Test + void test_has_function_full_name_method_select() { + IdentifierTree memberA = new IdentifierTreeImpl(meta, "A"); + IdentifierTree memberB = new IdentifierTreeImpl(meta, "B"); + Tree methodSelectAB = new MemberSelectTreeImpl(meta, memberA, memberB); + Tree methodSelect = new MemberSelectTreeImpl(meta, methodSelectAB, identifierTree); + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, methodSelect, args); + + assertThat(hasFunctionCallFullNameIgnoreCase(tree)).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "function")).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "A")).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "B")).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "A", "B")).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "A", "function")).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "B", "function")).isFalse(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "A", "B", "function")).isTrue(); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "A", "B", "function" , "C")).isFalse(); + } + + @Test + void test_has_function_full_name_unknown() { + Tree nativeNode = new NativeTreeImpl(meta, new TypeNativeKind(), null); + FunctionInvocationTree tree = new FunctionInvocationTreeImpl(meta, nativeNode, args); + assertThat(hasFunctionCallFullNameIgnoreCase(tree, "function")).isFalse(); + } + + @Test + void test_get_strings_tokens_returns_tokens() { + String code = "void fun fooBar() {\n" + + " val a = \"one,two,three\"; \n"+ + " foo(\"one,two$four\"); \n"+ + "}"; + + FunctionDeclarationTree root = (FunctionDeclarationTree)CONVERTER.parse(code, null).children().get(0); + + Set tokens = FunctionUtils.getStringsTokens(root, ",|\\$"); + + assertThat(tokens).containsExactlyInAnyOrder("one", "two", "three", "four"); + } +} diff --git a/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/LanguageTest.java b/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/LanguageTest.java new file mode 100644 index 00000000..352c1271 --- /dev/null +++ b/slang-checks/src/test/java/org/sonarsource/slang/checks/utils/LanguageTest.java @@ -0,0 +1,71 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.checks.utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class LanguageTest { + + @Test + void default_scala_function_name() { + Pattern pattern = Pattern.compile(Language.SCALA_FUNCTION_OR_OPERATOR_NAMING_DEFAULT); + assertThat(pattern.matcher("print").matches()).isTrue(); + assertThat(pattern.matcher("printLn").matches()).isTrue(); + assertThat(pattern.matcher("method_=").matches()).isTrue(); + assertThat(pattern.matcher("parse_!").matches()).isTrue(); + assertThat(pattern.matcher("+").matches()).isTrue(); + assertThat(pattern.matcher("<<").matches()).isTrue(); + assertThat(pattern.matcher("print_ln").matches()).isFalse(); + assertThat(pattern.matcher("PRINT").matches()).isFalse(); + assertThat(pattern.matcher("_print").matches()).isFalse(); + assertThat(pattern.matcher("+print").matches()).isFalse(); + } + + @Test + void propertyDefaultValueTest() throws Exception { + + Field someString = LanguageTest.class.getDeclaredField("someString"); + Annotation[] annotations = someString.getAnnotations(); + + assertThat(annotations).hasSize(1); + + PropertyDefaultValues defaultValues = someString.getAnnotation(PropertyDefaultValues.class); + assertThat(defaultValues.value()).hasSize(3); + + assertThat(Arrays.stream(defaultValues.value()).map(PropertyDefaultValue::language).collect(Collectors.toList())) + .containsExactlyInAnyOrder(Language.GO, Language.RUBY, Language.SCALA); + + assertThat(Arrays.stream(defaultValues.value()).map(PropertyDefaultValue::defaultValue).collect(Collectors.toList())) + .containsExactlyInAnyOrder("go", "ruby", "scala"); + + } + + @PropertyDefaultValue(language = Language.GO, defaultValue = "go") + @PropertyDefaultValue(language = Language.SCALA, defaultValue = "scala") + @PropertyDefaultValue(language = Language.RUBY, defaultValue = "ruby") + private String someString = ""; +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/AllBranchesIdentical.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/AllBranchesIdentical.slang new file mode 100644 index 00000000..a2fa84be --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/AllBranchesIdentical.slang @@ -0,0 +1,24 @@ + + if (x) { foo; }; + if (x) { foo; } else { bar; }; + if (x) { foo; } else { foo; }; // Noncompliant +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + if (x) { foo; } else if (y) { foo; }; + + if (x) { foo; } else if (y) { foo; } else { bar }; + + if (x) { foo; } else if (y) { foo; } else { foo }; // Noncompliant +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + if (x) if (y) return foo else return foo else return bar; // Noncompliant +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + match (x) { }; + match (x) { 1 -> a; }; + match (x) { 1 -> a; else -> b; }; + match (x) { 1 -> a; else -> a; }; // Noncompliant +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + match (x) { 1 -> a; 2 -> a; else -> b; }; + match (x) { 1 -> a; 2 -> a; else -> a; }; // Noncompliant + match (x) { else -> b; }; // Compliant: only default case diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadClassName.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadClassName.slang new file mode 100644 index 00000000..f0add39a --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadClassName.slang @@ -0,0 +1,12 @@ +class myClass{} // Noncompliant {{Rename class "myClass" to match the regular expression ^[A-Z][a-zA-Z0-9]*$.}} +// ^^^^^^^ + +class My_Class{} // Noncompliant {{Rename class "My_Class" to match the regular expression ^[A-Z][a-zA-Z0-9]*$.}} + +class my_class{} // Noncompliant {{Rename class "my_class" to match the regular expression ^[A-Z][a-zA-Z0-9]*$.}} + +class MyClass{} // Compliant + +class MyClassC{} // Compliant + +class MyClass${} // Noncompliant {{Rename class "MyClass$" to match the regular expression ^[A-Z][a-zA-Z0-9]*$.}} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadFunctionName.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadFunctionName.slang new file mode 100644 index 00000000..6506d25e --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadFunctionName.slang @@ -0,0 +1,4 @@ +void fun fooBar() {} // OK +fun () {} // OK +void fun foo_bar() {} // Noncompliant {{Rename function "foo_bar" to match the regular expression ^[a-z][a-zA-Z0-9]*$}} +// ^^^^^^^ diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadFunctionName.uppercase.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadFunctionName.uppercase.slang new file mode 100644 index 00000000..340e8f96 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BadFunctionName.uppercase.slang @@ -0,0 +1,7 @@ +void fun FOOBAR() {} // OK +void fun foo() {} // Noncompliant {{Rename function "foo" to match the regular expression ^[A-Z]*$}} +// ^^^ + +class A { + fun constructor() {} // Compliant, constructor +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/BooleanInversion.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BooleanInversion.slang new file mode 100644 index 00000000..a88a16ee --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BooleanInversion.slang @@ -0,0 +1,11 @@ +if (!(a == 2)) { }; // Noncompliant {{Use the opposite operator ("!=") instead.}} +// ^^^^^^^^^ +!(i < 10); // Noncompliant {{Use the opposite operator (">=") instead.}} +!(i > 10); // Noncompliant {{Use the opposite operator ("<=") instead.}} +!(i != 10); // Noncompliant {{Use the opposite operator ("==") instead.}} +!(i <= 10); // Noncompliant {{Use the opposite operator (">") instead.}} +!(i >= 10); // Noncompliant {{Use the opposite operator ("<") instead.}} + +if (a != 2) { }; +(i >= 10); +!(a + i); \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/BooleanLiteral.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BooleanLiteral.slang new file mode 100644 index 00000000..af86cfa7 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/BooleanLiteral.slang @@ -0,0 +1,48 @@ +x == true; // OK - as for now without semantic we do not know if x is nullable or a primitive +x == false; // OK - as for now without semantic we do not know if x is nullable or a primitive +x != true; // OK - as for now without semantic we do not know if x is nullable or a primitive +x != false; // OK - as for now without semantic we do not know if x is nullable or a primitive +true == x; // OK - as for now without semantic we do not know if x is nullable or a primitive +false == x; // OK - as for now without semantic we do not know if x is nullable or a primitive +true != x; // OK - as for now without semantic we do not know if x is nullable or a primitive +false != x; // OK - as for now without semantic we do not know if x is nullable or a primitive +!true; // Noncompliant {{Remove the unnecessary Boolean literal.}} ++true; +!false; // Noncompliant {{Remove the unnecessary Boolean literal.}} +false && foo; // Noncompliant {{Remove the unnecessary Boolean literal.}} +x || true; // Noncompliant {{Remove the unnecessary Boolean literal.}} +x || ((true)); // Noncompliant {{Remove the unnecessary Boolean literal.}} + +!x; // OK +x || foo; // OK +x == y; // OK +z != x; // OK + +x = if (foo) y else false; // Noncompliant {{Remove the unnecessary Boolean literal.}} +x = if (foo) y else true; // Noncompliant {{Remove the unnecessary Boolean literal.}} +x = if (foo) true else y; // Noncompliant {{Remove the unnecessary Boolean literal.}} +x = if (foo) false else y; // Noncompliant {{Remove the unnecessary Boolean literal.}} +x = if (foo) false; // Noncompliant {{Remove the unnecessary Boolean literal.}} + +x = if (foo) x else y; // OK + +x = if (foo) false + else { doSomething(); y }; + +x = if (foo) false + else if (bar) { doSomething(); y } + else true; + +x = if (foo) { doSomething(); y } + else true; + +x = if (a) true // Noncompliant + else false; + +x = if (a) true + else if (b) false + else false; + +x = if (b) + if (a) true // Noncompliant + else false; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/CodeAfterJump.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/CodeAfterJump.slang new file mode 100644 index 00000000..273ae75f --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/CodeAfterJump.slang @@ -0,0 +1,53 @@ +fun testReturn() { + return 42; // Noncompliant {{Refactor this piece of code to not have any dead code after this "return".}} +//^^^^^^^^^ + 42; +} + +fun lastReturn() { + 42; + return 42; +} + +fun testBreak() { + while (cond) { + break; // Noncompliant {{Refactor this piece of code to not have any dead code after this "break".}} +// ^^^^^ + 42; + 42; + } +} + +fun testContinue() { + while (cond) { + continue; // Noncompliant {{Refactor this piece of code to not have any dead code after this "continue".}} +// ^^^^^^^^ + 42; + } +} + +fun testContinueWithLabel() { + while (cond) { + continue myLabel; // Noncompliant {{Refactor this piece of code to not have any dead code after this "continue".}} +// ^^^^^^^^^^^^^^^^ + 42; + } +} + +fun empty() { +} + +fun fnRequiresCfg() { + if (cond) { + return; + } else { + return; + }; + + 42; +} + +fun testThrow() { + throw 42; // Noncompliant {{Refactor this piece of code to not have any dead code after this "throw".}} + 42; +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/CollapsibleIfStatements.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/CollapsibleIfStatements.slang new file mode 100644 index 00000000..088553e1 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/CollapsibleIfStatements.slang @@ -0,0 +1,59 @@ +if (a) { + print(a); + print(b); +}; + +if (a) { +} else if (b) { + if (c) { + } +} else { +}; + +if (a) + if (b) { + } else { + }; + + if (a) { // Noncompliant {{Merge this "if" statement with the nested one.}} +// ^^ + if (b) {} +// ^^< + }; + +if (a) { +} else { + if (c) { // Noncompliant {{Merge this "if" statement with the nested one.}} + if (b) {} + } +}; + +if (a) // Noncompliant {{Merge this "if" statement with the nested one.}} + if (b) + print(); + + +if (a) { // Noncompliant + if (b) { + } +}; + +if (a) { // Noncompliant + if (b) { // Noncompliant + if (c) { + } + } +}; + +if (a) { // Noncompliant + if (b) { + if (c) { + } else { + } + } +}; + +if (a) // Noncompliant + if (b) { + }; + diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/CommentedCode.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/CommentedCode.slang new file mode 100644 index 00000000..b6739086 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/CommentedCode.slang @@ -0,0 +1,14 @@ +// Noncompliant@+2 {{Remove this commented out code.}} +foo(); +// if(a) { print(b); }; +foo(); +// this is a normal comment +bar(); +// Noncompliant@+2 +a = 1; +/* + fun foo() { + print(a); + if (a) return "hello world!" + } +*/ diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/DuplicateBranch.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/DuplicateBranch.slang new file mode 100644 index 00000000..288d1368 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/DuplicateBranch.slang @@ -0,0 +1,97 @@ + if (x) { + foo; + foo; + }; + + if (x) { + foo; + foo; + } else { + bar; + bar; + }; + + if (x) { // handled by S3923 + foo; + foo; + } else { + foo; + foo; + }; + + if (x) { + foo; + foo; + } else if (y) { // Noncompliant {{This branch's code block is the same as the block for the branch on line 22.}} +// ^[el=+4;ec=3] + foo; + foo; + } else { + bar; + bar; + }; + + if (x) { + foo; + foo; + } else if (y) { + bar; + bar; + } else { // Noncompliant + bar; + bar; + }; + + if (x) { + + } else if (y) { + + } else { + bar; bar; + }; + + if (x) { + foo; foo; + } else if (y) { + foo; foo; + } else { + bar; bar; + }; + + if (x) + foo + else if (y) + foo + else + bar + ; + + if (x) + foo + + bar + else if (y) + foo // Noncompliant + + bar + else + bar + ; + + match(x) { + 1 -> + foo + + bar; + 2 -> + foo + + baz; + 3 -> + foo // Noncompliant + + bar; + }; + + match(x) { + 1 -> ; + 2 -> + foo + + baz; + 3 -> ; + }; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/DuplicatedFunctionImplementation.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/DuplicatedFunctionImplementation.slang new file mode 100644 index 00000000..cc1b7566 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/DuplicatedFunctionImplementation.slang @@ -0,0 +1,101 @@ +void fun foobar() {} +int fun foo_bar() {} // Compliant - has no line + +fun smallF1() { + foo = 1; + bar = foo > 3 || bar +} + +fun smallF2() { // Noncompliant + foo = 1; + bar = foo > 3 || bar +} + +string fun f1() { +// ^^> + foobar = "abc"; + foo = 1; + bar = foo > 3 || bar +} + +int fun f2() { + foobar = "abc"; + foo = 1; + baz = foo > 3 || bar +} + +boolean fun f3() { // Noncompliant {{Update this function so that its implementation is not identical to "f1" on line 14.}} +// ^^ + foobar = "abc"; + foo = 1; + bar = foo > 3 || bar +} + +fun f4() { // Noncompliant {{Update this function so that its implementation is not identical to "f1" on line 14.}} +// ^^ + foobar = "abc"; + foo = 1; + bar = foo > 3 || bar +} + +fun f5(a) { // Compliant - different parameter list + foobar = "abc"; + foo = 1; + bar = foo > 3 || bar +} + +fun f6() { + foo = 1; +} + +fun f7() { // Compliant - only 1 line + foo = 1; +} + +fun f8(int a) { // Compliant + foobar = "abc"; + foo = 1; + bar = foo > 3 || bar +} + +fun f9(int a) { // Noncompliant + foobar = "abc"; foo = 1; bar = foo > 3 || bar +} + +fun f10(string a) { // Compliant - not same parameter type + foobar = "abc"; + foo = 1; + bar = foo > 3 || bar +} + +fun(int a, int b) { // Compliant - not same parameters + foobar = "abc"; + foo = 1; + bar = foo > 3 || bar +} + +fun f11(int a, int b) { + foobar = "abcdefg"; + foo = 1; + bar = foo > 3 || bar +} + +fun f12(int a, int b) { + foobar = "abc"; + foo = 2; + bar = foo > 3 || bar +} + +class A { + + fun constructor(int a) { + foo = 1; + bar = 2; + } + + fun constructor(int a) { // Compliant, constructor + foo = 1; + bar = 2; + } + +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/ElseIfWithoutElse.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/ElseIfWithoutElse.slang new file mode 100644 index 00000000..d644ca39 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/ElseIfWithoutElse.slang @@ -0,0 +1,62 @@ +if (x == 0) { + x = 42; +}; + +if (x == 0) { + x = 42; +} else { + x = 43; +}; + +if (x == 0) { + doSomething(); +} else if (x == 1) { // Noncompliant {{Add the missing "else" clause.}} +//^^^^^^^ + doSomethingElse(); +}; + +if (x == 0) { + doSomething(); +} else if (x == 1) { + doSomethingElse(); +} else { + print("Something"); +}; + +if (x == 0) { + doSomething(); +} else if (x == 1) { + doSomethingElse(); +} else if (x == 2) { // Noncompliant {{Add the missing "else" clause.}} + print("Something"); +}; + +if (x == 0) { + break; +} else if (x == 1) { + return; +} else if (x == 3) { + throw(1); +}; + +if (x == 0) { + break; +} else if (x == 1) { // Noncompliant {{Add the missing "else" clause.}} +//^^^^^^^ + doSomething(); +}; + +if (x == 0) { +} else if (x == 1) { // Noncompliant {{Add the missing "else" clause.}} + return; +}; + +if (x >= 0) + if (x > 1) + return 0 + else if (x == 1) + doSomething() + else if (x == 0) { // Noncompliant {{Add the missing "else" clause.}} + doSomethingElse() + }; + diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyBlock.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyBlock.slang new file mode 100644 index 00000000..727d5573 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyBlock.slang @@ -0,0 +1,41 @@ +void fun a() {} + +void fun b() { + // comment +} + +fun () { + // comment +} + +fun () { + // Noncompliant@+1 + if (x > 0) { }; +} + +void fun c(int x) { + // Noncompliant@+1 + if (x > 0) { }; + + if (x > 1) { + // comment + }; + + match (x) { + // comment + }; + + // Noncompliant@+1 + match (x) { + + }; + + match (x) { + // Noncompliant@+1 + 1 -> { }; + 2 -> x; + else -> { x; }; + }; + + while (cond) {} // Compliant - exception to the rule +} \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyComment.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyComment.slang new file mode 100644 index 00000000..bd239b4d --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyComment.slang @@ -0,0 +1,15 @@ +/* Some comment */ + +/**/ // Noncompliant + + /* */ // Noncompliant +//^^^^^^ + +// Noncompliant@+1 +/* + +*/ + +// The next line should be compliant +// +// Comment line diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyFunction.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyFunction.slang new file mode 100644 index 00000000..7e47a5c4 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/EmptyFunction.slang @@ -0,0 +1,29 @@ +fun noBody(); + +fun notEmpty() { bar() } + +// Noncompliant@+1 +fun empty() {} +// ^^ + +fun containingOnlyAComment() { /* comment */ } + +fun emptyWithEndOfLineComment1() {} // end of line comment + +fun emptyWithEndOfLineComment2() { } /* comment */ + +fun emptyWithEndOfLineCommentOnMultipleLine() { } /* comment +*/ + +fun emptyOnSeveralLine() { +} // comment + +// Noncompliant@+1 +fun empty() { +} + +class A { + // Compliant, constructor + fun constructor() { + } +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/FixMeComment.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/FixMeComment.slang new file mode 100644 index 00000000..717cc162 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/FixMeComment.slang @@ -0,0 +1,51 @@ +// comment 1 + +// Noncompliant@+1 +// FIXME + +// Noncompliant@+1 +// FIXME just do it +// ^^^^^ + +// Noncompliant@+1 +// Fixme just do it + +// Noncompliant@+1 +// This is a FIXME just do it + +// This is not aFIXME comment + +/* + Multiline comment +*/ + +// Noncompliant@+2 +/* + FiXmE Multiline comment */ +//^^^^^ + +// Noncompliant@+2 +/* +fixme Multiline comment */ + +// Noncompliant@+1 +//fixme comment +//^^^^^ + +// notafixme comment + +// not2fixme comment + +// a fixmelist + +// Noncompliant@+1 +// fixme: things to do + +// Noncompliant@+1 +// :fixme: things to do + +// Noncompliant@+1 +// valid end of line fixme + +// Noncompliant@+1 +// valid end of file fixme \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/FunctionCognitiveComplexity.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/FunctionCognitiveComplexity.slang new file mode 100644 index 00000000..7023579f --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/FunctionCognitiveComplexity.slang @@ -0,0 +1,48 @@ +fun ok() { + if (x) { + if (y) { + foo(); + }; + }; + if (z) { + foo(); + }; +} + +fun ko() { // Noncompliant {{Refactor this method to reduce its Cognitive Complexity from 5 to the 4 allowed.}} [[effortToFix=1]] +// ^^ + if (x) { +//^^< {{+1}} + if (y) { +// ^^< {{+2 (incl 1 for nesting)}} + foo(); + }; + if (z) { +// ^^< {{+2 (incl 1 for nesting)}} + foo(); + }; + }; +} + +fun logical_operators() { // Noncompliant +// ^^^^^^^^^^^^^^^^^ + if (a +//^^< + && b && c +// ^^< + || d || e +// ^^< + && f +// ^^< + || g) { +// ^^< + foo(); + } +} + +fun nesting_anonymous() { // Noncompliant + fun() { + a && b || c && d || e && f; + } +} + diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/HardcodedCredentials.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/HardcodedCredentials.slang new file mode 100644 index 00000000..432871e3 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/HardcodedCredentials.slang @@ -0,0 +1,60 @@ +x = "pass"; +"pass"; +x = "password"; +"password"; +x = "login=a&password="; +"login=a&password="; +"login=a&password= " + value; +"login=a&password=a"; // Noncompliant +x = "login=a&password=xxx"; // Noncompliant {{"password" detected here, make sure this is not a hard-coded credential.}} +// ^^^^^^^^^^^^^^^^^^^^^^ +"login=a&password=xxx"; // Noncompliant +"login=a&passwd=xxx"; // Noncompliant {{"passwd" detected here, make sure this is not a hard-coded credential.}} +"login=a&pwd=xxx"; // Noncompliant {{"pwd" detected here, make sure this is not a hard-coded credential.}} +"login=a&passphrase=xxx"; // Noncompliant {{"passphrase" detected here, make sure this is not a hard-coded credential.}} + variableNameWithPasswordInIt = "xxx"; // Noncompliant {{"Password" detected here, make sure this is not a hard-coded credential.}} +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +variableNameWithPasswdInIt = "xxx"; // Noncompliant +variableNameWithPasswdInIt += "xxx"; // Noncompliant +variableNameWithPwdInIt = "xxx"; // Noncompliant {{"Pwd" detected here, make sure this is not a hard-coded credential.}} +A.variableNameWithPwdInIt = "xxx"; // Noncompliant {{"Pwd" detected here, make sure this is not a hard-coded credential.}} +A.B.variableNameWithPwdInIt = "xxx"; // Noncompliant {{"Pwd" detected here, make sure this is not a hard-coded credential.}} +otherVariableNameWithPasswordInIt; +variableNameWithPasswordInIt = native[] { [ "NativeFunctionCall" ] }; +val constValue = "login=a&password=xxx"; // Noncompliant +var passwd = "xxxx"; // Noncompliant +var passphrase = "xxx"; // Noncompliant +var okVariable = "xxxx"; +variableNameWithPasswdInIt = ""; +A.B.variableNameWithPwdInIt = ""; +var passwd = ""; +var passwd = 2; + +// No issue is raised when the matched wordlist item is present in both symbol name and literal string value. +var password = "password"; // Compliant +var myPassword = "users/connection.secretPassword"; // Compliant +myPassword = "users/connection.secretPassword"; // Compliant +var myPassword = "secretPasswd"; // Noncompliant {{"Password" detected here, make sure this is not a hard-coded credential.}} +myPassword = "secretPasswd"; // Noncompliant {{"Password" detected here, make sure this is not a hard-coded credential.}} +var params = "user=admin&password=Password123"; // Noncompliant {{"password" detected here, make sure this is not a hard-coded credential.}} + +// Database queries are compliant +var query = "password=?"; +query = "password=:password"; +query = "password=:param"; +query = "password='" + password + "'"; +query = "password=%s"; +query = "password=%v"; + +// String format is compliant +query = "password={0}"; + +// Support URI +var uri = "http://user:azer:ty123@domain.com"; // Noncompliant +var uri = "https://:azerty123@domain.com/path"; // Noncompliant {{Review this hard-coded URL, which may contain a credential.}} +var uri = "http://anonymous:anonymous@domain.com"; // Compliant, user and password are the same +var uri = "http://user:@domain.com"; +var uri = "http://user@domain.com:80"; +var uri = "http://domain.com/user:azerty123"; +var uri = "too-long-url-scheme://user:123456@server.com"; +var uri = "https:// invalid::url::format"; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/HardcodedIp.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/HardcodedIp.slang new file mode 100644 index 00000000..3c19146d --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/HardcodedIp.slang @@ -0,0 +1,86 @@ +x = 120; +"120"; +ip = "1.2.3.4"; // Noncompliant {{Make sure using this hardcoded IP address is safe here.}} +// ^^^^^^^^^ +"1.2.3.4"; // Noncompliant +"1.2.3.4:80"; // Noncompliant +"1.2.3.4:8080"; // Noncompliant +"1.2.3.4:a"; +"1.2.3.4.5"; + +// Noncompliant@+1 {{Make sure using this hardcoded IP address is safe here.}} +url = "http://192.168.0.1/admin.html"; +// Noncompliant@+1 +url = "http://192.168.0.1:8181/admin.html"; +url2 = "http://www.example.org"; + +notAnIp1 = "0.0.0.1234"; +notAnIp2 = "1234.0.0.0"; +notAnIp3 = "1234.0.0.0.0.1234"; +notAnIp4 = ".0.0.0.0"; +notAnIp5 = "0.256.0.0"; + +ip = "0.00.0.0"; // Compliant +ip = "1.2.03.4"; // Compliant + +fileName = "v0.0.1.200__do_something.sql"; // Compliant - suffixed and prefixed +version = "1.0.0.0-1"; // Compliant - suffixed + +"1080:0:0:0:8:800:200C:417A"; // Noncompliant {{Make sure using this hardcoded IP address is safe here.}} +"[1080::8:800:200C:417A]"; // Noncompliant +"::800:200C:417A"; // Noncompliant +"1080:800:200C::"; // Noncompliant +"::FFFF:129.144.52.38"; // Noncompliant +"::129.144.52.38"; // Noncompliant +"::FFFF:38"; // Noncompliant +"::100"; // Noncompliant +"1080:0:0:0:8:200C:131.107.129.8"; // Noncompliant +"1080:0:0::8:200C:131.107.129.8"; // Noncompliant + +"1080:0:0:0:8:800:200C:417G"; // Compliant - not valid IPv6 +"1080:0:0:0:8::800:200C:417A"; // Compliant - not valid IPv6 +"1080:0:0:0:8:::200C:417A"; // Compliant - not valid IPv6 +"1080:0:0:0:8"; // Compliant - not valid IPv6 +"1080:0::0:0:8::200C:417A"; // Compliant - not valid IPv6 +"1080:0:0:0:8::200C:417A:"; // Compliant - not valid IPv6 +"1080:0:0:0:8::200C:131.107.129.8"; // Compliant - not valid IPv6 +"1080:0:0:0:8::200C:256.256.129.8"; // Compliant - not valid IPv6 +"1080:0:0:0:8:200C:200C:131.107.129.8"; // Compliant - not valid IPv6 +"1080:0:0:0:8:131.107.129.8"; // Compliant - not valid IPv6 +"1080:0::0::8:200C:131.107.129.8"; // Compliant - not valid IPv6 +"1080:0:0:0:8:200C:131.107.129"; // Compliant - not valid IPv6 +"1080:0:0:0:8:200C:417A:131.107"; // Compliant - not valid IPv6 + +// Noncompliant@+1 {{Make sure using this hardcoded IP address is safe here.}} +"http://[2002:db8:1f70::999:de8:7648:6e8]"; +// Noncompliant@+1 +"http://[2002:db8:1f70::999:de8:7648:6e8]:100/"; +// Noncompliant@+1 + "https://[3FFE:1A05:510:1111:0:5EFE:131.107.129.8]:8080/"; +//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +// Noncompliant@+1 +"https://[3FFE::1111:0:5EFE:131.107.129.8]:8080/"; + +"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; // Noncompliant + +// Exceptions +"0.0.0.0"; +"::1"; +"000:00::1"; +"255.255.255.255"; +"255.255.255.255:80"; +"2.5.255.255"; +"127.5.255.255"; +"http://[::0]:100/"; +"0000:0000:0000:0000:0000:0000:0000:0000"; +"192.0.2.0"; +"198.51.100.0"; +"203.0.113.0"; +"2001:db8:3:4:5:6:7:8"; +"::ffff:0:127.0.0.1"; +"::ffff:0:127.100.150.200"; +"::ffff:0:127.255.255.255"; +"::ffff:127.0.0.1"; +"::ffff:127.100.150.200"; +"::ffff:127.255.255.255"; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/IdenticalBinaryOperand.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/IdenticalBinaryOperand.slang new file mode 100644 index 00000000..28805d7e --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/IdenticalBinaryOperand.slang @@ -0,0 +1,21 @@ + x == 1; + 1 == 1; // Noncompliant {{Correct one of the identical sub-expressions on both sides this operator}} + 1 == (1); // Noncompliant {{Correct one of the identical sub-expressions on both sides this operator}} + (1 + 2) == 1 + 2; // Noncompliant + (1 + 2) == ((1 + 2)); // Noncompliant + (1 + 2) == ((1 + 2 + 3)); + x +//^> + == x; // Noncompliant +// ^ + 1 == 2; + x = x; + x + x; + x * x; + x <= x; // Noncompliant + _x <= _x; // Noncompliant + x_ <= x_; // Noncompliant + _x <= x_; + x <= _; + _ <= y; + _ <= _; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/IdenticalConditions.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/IdenticalConditions.slang new file mode 100644 index 00000000..ee2ee132 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/IdenticalConditions.slang @@ -0,0 +1,26 @@ +// if + +if (x) { +} else if (x) { // Noncompliant {{This condition duplicates the one on line 3.}} +}; + +if (x) {}; +if (x) {} else {}; +if (x) {} else if (y) {}; +if (x) {} else if (x) {}; // Noncompliant +// ^> ^ +if (x) {} else if (y) {} else if (z) {}; +if (x) {} else if (y) {} else if (y) {}; // Noncompliant +// ^> ^ +if (x) {} else if (x) {} else if (x) {}; // Noncompliant 2 +if (x) {} else if (y) {} else if ((y)) {}; // Noncompliant + + +// match + +match (x) { 1 -> a; }; +match (x) { 1 -> a; else -> b; }; +match (x) { 1 -> a; 2 -> b; }; +match (x) { 1 -> a; 1 -> b; }; // Noncompliant +// ^> ^ +match (x) { 1 -> a; (1) -> b; }; // Noncompliant diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalse.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalse.slang new file mode 100644 index 00000000..5c6c9479 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/IfConditionalAlwaysTrueOrFalse.slang @@ -0,0 +1,38 @@ +if (true) { // Noncompliant {{Remove this useless "if" statement.}} +// ^^^^ + return 1 +}; + +if (false) { // Noncompliant + return 1 +}; + +if (condition) { + return 1 +} else if (true) { // Noncompliant + return 1 +}; + +if (true) // Noncompliant + return 1; + +if (((true))) // Noncompliant + return 1; + +if (!true) // Noncompliant + return 1 +else if (cond && false) { // Noncompliant + return 1 +} else if (cond || false) + return 1 +else if (cond1 || cond2 || true) // Noncompliant + return 1 +else if (cond && cond2 && !true && cond3) { // Noncompliant + return 1 +} else if (cond && !(cond2 && (!true && cond3))) { // Noncompliant + return 1 +}; + + +x = if (true) 1 else 2; // Noncompliant +x = if (!condition) 1 else 2; \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchCaseTooBig_3.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchCaseTooBig_3.slang new file mode 100644 index 00000000..f91b21e7 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchCaseTooBig_3.slang @@ -0,0 +1,25 @@ +match (x) { + 1 -> { // Noncompliant {{Reduce this case clause number of lines from 4 to at most 3, for example by extracting code into methods.}} +// ^^^^ + a = 1; + print(a); + }; + else -> b; +}; + +match (x) { + 1 -> foo(); + else -> { // Noncompliant {{Reduce this case clause number of lines from 4 to at most 3, for example by extracting code into methods.}} +// ^^^^^^^ + a = 1; + print(a); + }; +}; + +match (x) { + 1 -> foo(); // OK + // comments should be ignored + // another comment + + else -> b; +}; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchCaseTooBig_5.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchCaseTooBig_5.slang new file mode 100644 index 00000000..3ec0825a --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchCaseTooBig_5.slang @@ -0,0 +1,17 @@ +match (x) { + expression -> { // Noncompliant {{Reduce this case clause number of lines from 8 to at most 5, for example by extracting code into methods.}} + // ^^^^^^^^^^^^^ + a = 1; + foo(); + bar(); + if (a == 1) { + print(1); + }; + }; + else -> b; +}; + +match (x) { + 1 -> foo(); // OK + else -> b; +}; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchWithoutElse.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchWithoutElse.slang new file mode 100644 index 00000000..b5942513 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/MatchWithoutElse.slang @@ -0,0 +1,41 @@ + match (x) { }; // Noncompliant {{Add a default clause to this "match" statement.}} +//^^^^^ + match (x) { 1 -> 1; }; // Noncompliant {{Add a default clause to this "match" statement.}} + match (x) { 1 -> 1; else -> 2; }; + match (x) { else -> 2; 1 -> 1; }; + + int fun foo() { + match (x) { 1 -> 1; }; // Noncompliant + match (x) { 1 -> 1; else -> 2; }; + + int val value = match (x) { 1 -> 1; }; // Noncompliant + value = if (y) { match (x) { 1 -> 1; }; } else 2; // Noncompliant + + while (y) { + match (x) { else -> 2; }; + match (x) { 1 -> 1; }; // Noncompliant + }; + + native[] { [ + match (x) { // Noncompliant + 1 -> { match (y) { // Noncompliant + 1 -> 1; + 2 -> { + var c; + match (z) { // Noncompliant + 1 -> { c = 2; }; + }; + }; + 3 -> { + var variable; + match (c) { + 1 -> variable = 1; + else -> variable = 2; + }; + variable; + }; + }; + }; + } + ] } + } diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/NestedMatch.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/NestedMatch.slang new file mode 100644 index 00000000..034b0f6b --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/NestedMatch.slang @@ -0,0 +1,47 @@ +match (x) { 1 -> a; else -> b; }; // OK + +match (x) { + 1 -> a; + 2 -> match (y) { // Noncompliant {{Refactor the code to eliminate this nested "match".}} + // ^^^^^ + 3 -> c; + else -> d; + }; + else -> b; +}; + +match (x) { + 1 -> a; + 2 -> { + match (y) { // Noncompliant {{Refactor the code to eliminate this nested "match".}} + 3 -> c; + else -> d; + }; + match (z) { // Noncompliant {{Refactor the code to eliminate this nested "match".}} + 3 -> c; + else -> d; + }; + }; + else -> b; +}; + +match (x) { + 1 -> a; + 2 -> match (y) { // Noncompliant {{Refactor the code to eliminate this nested "match".}} + 3 -> c; + else -> match (z) { // Noncompliant {{Refactor the code to eliminate this nested "match".}} + 4 -> d; + else -> e; + }; + }; + else -> b; +}; + +match (x) { + 1 -> a; + 2 -> b; + else -> match (y) { // Noncompliant {{Refactor the code to eliminate this nested "match".}} + 3 -> c; + else -> d; + }; +}; \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/OctalValues.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/OctalValues.slang new file mode 100644 index 00000000..9feade64 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/OctalValues.slang @@ -0,0 +1,17 @@ +17000; + + 02522; // Noncompliant {{Use decimal values instead of octal ones.}} +// ^^^^^ +0o2522; // Noncompliant +0O2522; // Noncompliant + +"0o2522"; +"02522"; +"0O2522"; + +a + 022; // Noncompliant +// ^^^ + +02; // Compliant - part of exceptions +0077; // Compliant - part of exceptions +0o077; // Compliant - part of exceptions \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/OneStatementPerLine.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/OneStatementPerLine.slang new file mode 100644 index 00000000..f14ffda5 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/OneStatementPerLine.slang @@ -0,0 +1,41 @@ +print("Hello World!"); print("Hello World!"); //Noncompliant {{Reformat the code to have only one statement per line.}} + +if (a) {}; if (b) {}; // Noncompliant +// ^^^^^^^^^ + +fun foo() {} fun foo() {} // Noncompliant +// ^^^^^^^^^^^^ + +fun foo() { + if (a) {}; if (b) {}; // Noncompliant +// ^^^^^^^^^ +} + +if (a) { + a(); b(); // Noncompliant +// ^^^ +}; + +a(); b(); c(); // Noncompliant +// ^^^ ^^^< + +if(a) { foo(); // FN + b = 2; +}; + +val a = 1; val b = 1; // Noncompliant + +match (x) { + 1 -> { + a(); b(); // Noncompliant + }; + else -> print(1); +}; + +fun foo() { print(); } // OK + +fun foo() { print(); + foo(); +} // OK + +if (a) { b; }; // OK \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/RedundantParentheses.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/RedundantParentheses.slang new file mode 100644 index 00000000..c15c4aa8 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/RedundantParentheses.slang @@ -0,0 +1,15 @@ +x = x + 1; +x = (x + 1); +x = ((x + 1)); // Noncompliant +// ^ ^< +x = (((x + 1))); // Noncompliant 2 +x = (x + 1) * (x + 2); +x = x + (1 * x) + 2; +x = x + ((1 * x)) + 2; // Noncompliant +x = (1); +x = ((1)); // Noncompliant +// ^ ^< + +if (x && (x + 1 > 0)) {}; +if (x && ((x + 1 > 0))) {}; // Noncompliant +if (x && ((x + 1) > 0)) {}; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/SelfAssignment.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/SelfAssignment.slang new file mode 100644 index 00000000..f107114a --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/SelfAssignment.slang @@ -0,0 +1,14 @@ + x = 1; + x = x + 1; + x += x; + x = x; // Noncompliant {{Remove or correct this useless self-assignment.}} +//^^^^^ + native[] { [x] } = x; // Ex: this.x = x + + // Ex: this.x = this.x + // Noncompliant@+1 + native[] { [x] } = native[] { + [ + x; + ] + }; \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/StringLiteralDuplicated.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/StringLiteralDuplicated.slang new file mode 100644 index 00000000..060cf802 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/StringLiteralDuplicated.slang @@ -0,0 +1,34 @@ +x = "string literal1"; // Noncompliant {{Define a constant instead of duplicating this literal "string literal1" 3 times.}} [[effortToFix=2]] +// ^^^^^^^^^^^^^^^^^ +x += "string literal1" + "other string literal"; +// <^^^^^^^^^^^^^^^^^ +native[] { [] } = native[] { + [ + "string literal1" +// <^^^^^^^^^^^^^^^^^ + ] +}; + +void fun function1(string abcde) { + v = "string literal2" + "string literal2" // Compliant - literal only appears twice +} +"string literal3"; "string literal3"; +"string literal3${x}"; // Compliant - string entries of string templates not considered as string literals + +void fun funtcion2(int abcde) { + if (abcde == "string literal4") { // Noncompliant {{Define a constant instead of duplicating this literal "string literal4" 5 times.}} [[effortToFix=4]] +// ^^^^^^^^^^^^^^^^^ + } +} + +match("string literal4") { +// <^^^^^^^^^^^^^^^^^ + 1 -> "string literal4"; +// <^^^^^^^^^^^^^^^^^ + "string literal4" -> "string literal4"; +// <^^^^^^^^^^^^^^^^^ <^^^^^^^^^^^^^^^^^ +}; + +"abcd"; "abcd"; "abcd"; "abcd"; // Compliant - string length smaller than threshold +"string_literal5"; "string_literal5"; +"string_literal5"; "string_literal5"; // Compliant - single word \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/StringLiteralDuplicated.threshold_4.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/StringLiteralDuplicated.threshold_4.slang new file mode 100644 index 00000000..137c7828 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/StringLiteralDuplicated.threshold_4.slang @@ -0,0 +1,5 @@ + "string literal1"; "string literal1"; "string literal1"; // Compliant - only appears 3 times which is less than the custom threshold of 4 + "string literal2"; // Noncompliant {{Define a constant instead of duplicating this literal "string literal2" 4 times.}} +//^^^^^^^^^^^^^^^^^ + "string literal2"; "string literal2"; "string literal2"; +//<^^^^^^^^^^^^^^^^^ <^^^^^^^^^^^^^^^^^ <^^^^^^^^^^^^^^^^^ diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/Tabs_compliant.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/Tabs_compliant.slang new file mode 100644 index 00000000..12b26e56 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/Tabs_compliant.slang @@ -0,0 +1,3 @@ +a = 1; +b = 4; + x = 1; \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/Tabs_noncompliant.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/Tabs_noncompliant.slang new file mode 100644 index 00000000..bf74e261 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/Tabs_noncompliant.slang @@ -0,0 +1,3 @@ +// Noncompliant@0 {{Replace all tab characters in this file "Tabs_noncompliant.slang" by sequences of white-spaces.}} + a = 1; + tabOnTheLeft = 1; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TodoComment.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TodoComment.slang new file mode 100644 index 00000000..f1b0565e --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TodoComment.slang @@ -0,0 +1,51 @@ +// comment 1 + +// Noncompliant@+1 +// TODO + +// Noncompliant@+1 +// TODO just do it +// ^^^^ + +// Noncompliant@+1 +// Todo just do it + +// Noncompliant@+1 +// This is a TODO just do it + +// This is not aTODO comment + +/* + Multiline comment +*/ + +// Noncompliant@+2 +/* + TODO Multiline comment */ +//^^^^ + +// Noncompliant@+2 +/* +TODO Multiline comment */ + +// Noncompliant@+1 +//todo comment +//^^^^ + +// notatodo comment + +// not2todo comment + +// a todolist + +// Noncompliant@+1 +// todo: things to do + +// Noncompliant@+1 +// :TODO: things to do + +// Noncompliant@+1 +// valid end of line todo + +// Noncompliant@+1 +// valid end of file todo \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooComplexExpression_2.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooComplexExpression_2.slang new file mode 100644 index 00000000..dc90567f --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooComplexExpression_2.slang @@ -0,0 +1,3 @@ + a && b; + a && b || c; + a && b || c && d; // Noncompliant {{Reduce the number of conditional operators (3) used in the expression (maximum allowed 2).}} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooComplexExpression_3.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooComplexExpression_3.slang new file mode 100644 index 00000000..6f38b096 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooComplexExpression_3.slang @@ -0,0 +1,12 @@ + a && b; + a && b || c; + a && b || c && d; + a && b || c && d || e; // Noncompliant {{Reduce the number of conditional operators (4) used in the expression (maximum allowed 3).}} [[effortToFix=1]] + a && b || c && d || e && f; // Noncompliant [[effortToFix=2]] +//^^^^^^^^^^^^^^^^^^^^^^^^^^ +if (a && b || c && d || e && f) {}; // Noncompliant +if (a && (b || c) && (d || e && f)) {}; // Noncompliant +if (a && b || c) {}; +if (!(a && b || c && d)) {}; +if (!(a && b || c && d || e)) {}; // Noncompliant +foo(a && b) && foo(a || b) && foo(a && b); diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooDeeplyNestedStatements.max_2.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooDeeplyNestedStatements.max_2.slang new file mode 100644 index 00000000..62d8b012 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooDeeplyNestedStatements.max_2.slang @@ -0,0 +1,13 @@ + if (true) {} +//^^> + else if (true) {} + else if (true) { + if (true) { // Compliant +// ^^> + if (true) {// Noncompliant {{Refactor this code to not nest more than 2 control flow statements.}} +// ^^ + if (true) { + }; + }; + }; + }; \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooDeeplyNestedStatements.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooDeeplyNestedStatements.slang new file mode 100644 index 00000000..e2579fb8 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooDeeplyNestedStatements.slang @@ -0,0 +1,113 @@ +if (false) { // Compliant +}; + + if (true) {} +//^^> + else if (true) {} + else if (true) { + if (true) { // Compliant +// ^^> + if (true) { +// ^^> + if (true) { // Noncompliant {{Refactor this code to not nest more than 3 control flow statements.}} +// ^^ + }; + }; + }; + }; + +if (false) { // Compliant + if (true) { // Compliant + } else { + if (false) { // Compliant + if (true) { // Noncompliant {{Refactor this code to not nest more than 3 control flow statements.}} + if (false) { // Compliant + }; + } else if (true) { // Compliant + } else { + if (false) { // Compliant + }; + }; + }; + }; +}; + +if (false) { // Compliant +} else if (false) { // Compliant +} else if (false) { // Compliant +} else if (false) { // Compliant +} else if (false) { // Compliant +}; + +if (false) // Compliant + if (false) // Compliant + if (false) // Compliant + if (true) // Noncompliant + println(); + + for (int var x = list) { // Compliant +//^^^> + for (int var o = objects) { // Compliant +// ^^^> + while (false) { // Compliant +// ^^^^^> + for (int var p = list) { // Noncompliant + }; + + while (false){ // Noncompliant + }; + + do { } while (false); // Noncompliant + + if (false) { // Noncompliant + }; + + match (p) { // Noncompliant +// ^^^^^ + }; + + try { // Noncompliant + + } catch () { }; + + x = if (false) 1 else 2; + x = if (false) { // Noncompliant + foo(); + 1 + } else 2; + }; + + x = if (condition) if (x == 0) 1 else 2 else 3; + x = if (condition) (if (x == 0) 1 else 2) else 3; + if (if (x == 0) a() else b()) { + }; + + if (condition) if (x == 0) a() else b(); // Noncompliant + + for (int var o = objects) if (x == 0) 1 else 2; // Noncompliant + + for (int var x = if (x == 0) 1 else 2) { + }; + + while (if (x == 0) false else true) { + }; + + match (if (x == 0) 1 else 2) { + 1 -> if (x == 0) 1 else 2; // Noncompliant + else -> if (x == 0) 1 else 2; // Noncompliant + }; + + }; +}; + + +while (true) { + if (true) { + } else if (true) { + } else { + if (true) { + if (true) { // Noncompliant + }; + }; + }; +}; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongFunction_3.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongFunction_3.slang new file mode 100644 index 00000000..6c0f920f --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongFunction_3.slang @@ -0,0 +1,48 @@ +fun f1() { + a; a; +} + +fun f1() { // Noncompliant {{This function has 4 lines of code, which is greater than the 3 authorized. Split it into smaller functions.}} +// ^^ + a; + a; +} + +fun f1() { + + a; +} + +fun f1() { + // comment + a; +} + +int fun foo( // Compliant, no line of code +p1, +p2, +p3, +p4) +{ +} + +int fun foo( +p1, +p2, +p3, +p4); + +int fun ( +p1, +p2, +p3, +p4); + + int fun () // Noncompliant +// ^^^^^^^^^^ +{ + a; + a; + a; + a; +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongFunction_4.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongFunction_4.slang new file mode 100644 index 00000000..fcd37c5d --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongFunction_4.slang @@ -0,0 +1,11 @@ +fun f1() { // Noncompliant {{This function has 5 lines of code, which is greater than the 4 authorized. Split it into smaller functions.}} + a; + a; + a; +} + +fun f1() { + a; + + a; +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongLine_120.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongLine_120.slang new file mode 100644 index 00000000..aee01226 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongLine_120.slang @@ -0,0 +1,9 @@ +int fun (p1, p2) {} + +// short comment + +// Noncompliant@+1 {{Split this 124 characters long line (which is greater than 120 authorized).}} +int fun fooVeryLongName(fooVeryLongName1, fooVeryLongName2, fooVeryLongName3, fooVeryLongName4, fooVeryLongName5, p6, p7) {} + +// Noncompliant@+1 {{Split this 125 characters long line (which is greater than 120 authorized).}} +// this is a very comment that should raise an issue when its length is greater that 120 characters that is the default value \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongLine_40.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongLine_40.slang new file mode 100644 index 00000000..4cae4bb7 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooLongLine_40.slang @@ -0,0 +1,4 @@ +int fun (p1, p2, p3) {} + +// Noncompliant@+1 +int fun funWithLongName(p1, p2, p3) { println(10);} \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyCases_3.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyCases_3.slang new file mode 100644 index 00000000..0ed4227f --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyCases_3.slang @@ -0,0 +1,30 @@ + match (x) { + 1 -> 'a'; + 2 -> 'b'; + 3 -> 'c'; + }; + + match (x) { // Noncompliant {{Reduce the number of match branches from 4 to at most 3.}} + 1 -> 'a'; + 2 -> 'b'; + 3 -> 'c'; + 4 -> 'd'; + }; + + match (x) { + 1 -> 'a'; + 2 -> 'b'; + else -> 'c'; + }; + + match (x) { // Noncompliant +//^^^^^ + 1 -> 'a'; +// ^^^^< + 2 -> 'b'; +// ^^^^< + 3 -> 'c'; +// ^^^^< + else -> 'd'; +// ^^^^^^^< +}; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyCases_4.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyCases_4.slang new file mode 100644 index 00000000..f224cf75 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyCases_4.slang @@ -0,0 +1,14 @@ +match (x) { + 1 -> 'a'; + 2 -> 'b'; + 3 -> 'c'; + 4 -> 'd'; +}; + +match (x) { // Noncompliant + 1 -> 'a'; + 2 -> 'b'; + 3 -> 'c'; + 4 -> 'd'; + 5 -> 'e'; +}; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyLinesOfCodeFile.max_4.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyLinesOfCodeFile.max_4.slang new file mode 100644 index 00000000..f413e7b7 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyLinesOfCodeFile.max_4.slang @@ -0,0 +1,15 @@ +// Noncompliant@0 {{File "TooManyLinesOfCodeFile.max_4.slang" has 5 lines, which is greater than 4 authorized. Split it into smaller files.}} +// comment - not a line of code + +if (cond) { + a = b + 1 +}; + + +x = 4; + +x; + +/* + * There are 5 lines of code in this file + */ diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyLinesOfCodeFile.max_5.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyLinesOfCodeFile.max_5.slang new file mode 100644 index 00000000..164b830f --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyLinesOfCodeFile.max_5.slang @@ -0,0 +1,14 @@ +// comment - not a line of code + +if (cond) { + a = b + 1 +}; + + +x = 4; + +x; + +/* + * There are 5 lines of code in this file + */ diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyParameters.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyParameters.slang new file mode 100644 index 00000000..dc83e001 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyParameters.slang @@ -0,0 +1,17 @@ +int fun foo(p1, p2, p3, p4, p5, p6, p7) {} +int fun (p1, p2, p3, p4, p5, p6, p7) {} + +int fun (p1, p2, p3, p4, p5, p6, p7, p8) {} // Noncompliant +int fun foo(p1, p2, p3, p4, p5, p6, p7, p8) {} // Noncompliant {{This function has 8 parameters, which is greater than the 7 authorized.}} +// ^^^ ^^< + +int fun foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7) {} +int fun foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) {} // Noncompliant + +override int fun foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) {} // OK +private int fun foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) {} // Noncompliant +native [] {} int fun foo(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) {} // Noncompliant + +class A { + fun constructor(int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8) {} // Compliant, constructor +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyParameters.threshold.3.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyParameters.threshold.3.slang new file mode 100644 index 00000000..4edb5ccf --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/TooManyParameters.threshold.3.slang @@ -0,0 +1,3 @@ +int fun foo(p1, p2, p3) {} +int fun foo(p1, p2, p3, p4, p5, p6, p7, p8) {} // Noncompliant {{This function has 8 parameters, which is greater than the 3 authorized.}} +// ^^^ ^^< ^^< ^^< ^^< ^^< diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedFunctionParameter.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedFunctionParameter.slang new file mode 100644 index 00000000..0220e3a6 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedFunctionParameter.slang @@ -0,0 +1,51 @@ +void fun fooBar() {} // OK + +void fun fooBar(int a) {} // Noncompliant {{Remove this unused function parameter "a".}} +// ^ + +void fun fooBar(int a, int b) {} // Noncompliant {{Remove these unused function parameters.}} +// ^ ^< {{Remove this unused method parameter b".}} +// ^@-1< {{Remove this unused method parameter a".}} + +void fun fooBar(int a, int b) { // Noncompliant {{Remove this unused function parameter "b".}} +// ^ + x = a; +} + +void fun fooBar(int a, int b) { // Noncompliant {{Remove this unused function parameter "a".}} +// ^ + match (b) { } +} + +void fun fooBar(int a, b) { // OK + x = a; + match (b) { } +} + +id fun fooBar(int id) {} // OK - issue is not raised here as 'id' is found in method header. + // This is done to avoid FP in languages that can use parameters in method headers (ex: in initializers for kotlin) + +class A { + + private fun constructor(int a) { // Compliant to prevent FP on constructor + } + + void fun fooBar() {} + + void fun fooBar(int a, int b) { // OK - method is not private and could potentially be overridden + x = a; + } + + private void fun fooBar(int a, int b) { // Noncompliant + x = a; + } + + private override void fun fooBar(int a, int b) { // OK - even though it is private, it is an override method + x = a; + } + +} + +void fun main(int a) {} // OK - "main" functions are ignored +int fun Main(int a) {} // OK - "main" functions are ignored +fun foo(p1, native [] {} p2) {p1} // OK - parameter with modifier are ignored \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedLocalVariable.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedLocalVariable.slang new file mode 100644 index 00000000..ced478cb --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedLocalVariable.slang @@ -0,0 +1,17 @@ +int var global; // Compliant + +void fun fooBar() { + int val a = 0; // Compliant + + int val b; // Noncompliant {{Remove this unused "b" local variable.}} + // ^ + + int var c; // Noncompliant {{Remove this unused "c" local variable.}} + // ^ + + int var d; // Compliant + d = 0; + + int var e; // Compliant + e = d + a; +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedPrivateMethod.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedPrivateMethod.slang new file mode 100644 index 00000000..da249463 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/UnusedPrivateMethod.slang @@ -0,0 +1,70 @@ +class A { + private fun unusedPrivate() {} // Noncompliant + + public fun unusedPrivate(int a) {} + + private fun unusedPrivate(int a) {} // Noncompliant + + private override fun unusedPrivateOverride(int a) {} // OK - might be forced to override the method here + + private fun usedPrivate(int a) {} + + fun unusedNoModifier(int a) { + usedPrivate(); + } + + public fun unusedPublic(int a) {} +} + +class B { + private fun f1() {} + + fun g(int f1) {} // FN of current approach, "f1" is used as identifier elsewhere in the class + + private fun f2() {} + + fun h(C c) { native[] { [c;][f2();] } } // FN of current approach, another class has same identifier for a method call "f2" (c.f2();) + + private fun writeObject() {} // Noncompliant +} + +class C { + + private fun constructor() {} // Compliant to prevent FP, constructor call semantic is not yet supported + + private fun unusedPrivate1() {} // Noncompliant + + private fun unusedPrivate2() {} // FN of current approach, "unusedPrivate2" identifier is used in inner class + + private fun unusedPrivate4() {} + + private fun f1() { + f1(); + usedInnerMethod1(); + usedInnerMethod2(); + unusedPrivate4(); + } + + class D { + private fun unusedPrivate2() {} + + private fun unusedPrivate3() {} // Noncompliant + + private fun unusedPrivate4() {} // FN of current approach, "unusedPrivate4" identifier is used in outer class + + private fun f1() {} // FN of current approach, "f1" is used in outer class + + private fun usedInnerMethod1() {} + + private fun f2() { + f2(); + unusedPrivate2(); + } + + class E { + private fun unusedPrivate5() {} // Noncompliant + + private fun usedInnerMethod2() {} + } + } +} diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/VariableAndParameterName.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/VariableAndParameterName.slang new file mode 100644 index 00000000..a2892128 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/VariableAndParameterName.slang @@ -0,0 +1,32 @@ +var NOT_LOCAL; + +fun localVariables() { + var localVar; + var INVALID_LOCAL; // Noncompliant {{Rename this local variable to match the regular expression "^[_a-z][a-zA-Z0-9]*$".}} +// ^^^^^^^^^^^^^ + var invalid_local; // Noncompliant +} + +fun parameters(param1, PARAM2, param3) { // Noncompliant {{Rename this parameter to match the regular expression "^[_a-z][a-zA-Z0-9]*$".}} +// ^^^^^^ +} + +native [] { + [ + var POSSIBLY_NOT_LOCAL; + ] +}; + +class A { + fun constructor(param1, PARAM2) { // Noncompliant +// ^^^^^^ + } + fun method(param1, PARAM2) { // Noncompliant +// ^^^^^^ + } +} + +fun method(_) { } + +// testing corner case where the identifier syntax is not supported +fun method(__) { } \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/WrongAssignmentOperator.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/WrongAssignmentOperator.slang new file mode 100644 index 00000000..25ebded3 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/WrongAssignmentOperator.slang @@ -0,0 +1,27 @@ +target =-num; // Noncompliant {{Was "-=" meant instead?}} +// ^^ +target = + -num; +target = -num; // Compliant intent to assign inverse value of num is clear +target =--num; + +target += num; +target =+ num; // Noncompliant {{Was "+=" meant instead?}} +// ^^ +target = + + num; +target = + +num; +target = +num; +target =++num; +target=+num; // Compliant - no spaces between variable, operator and expression + +a = b != c; +a = b =! c; // Noncompliant {{Was "!=" meant instead?}} [[sc=11;ec=13]] +a = b =!! c; // Noncompliant +a = b = !c; +a =! c; // Noncompliant {{Add a space between "=" and "!" to avoid confusion.}} +a = ! c; +a = !c; +a = + !c; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Compliant.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Compliant.slang new file mode 100644 index 00000000..7c469c6c --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Compliant.slang @@ -0,0 +1,9 @@ +// copyright 2018 +// comment + +if (cond) { + a = b + 1 +}; + +x = 4; +x; \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Multiline.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Multiline.slang new file mode 100644 index 00000000..89950363 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Multiline.slang @@ -0,0 +1,13 @@ +/* + * SonarSource SLang + * Copyright (C) 1999-2001 SonarSource SA + * mailto:info AT sonarsource DOT com + */ +// comment + +if (cond) { + a = b + 1 +}; + +x = 4; +x; diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/NoFirstLine.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/NoFirstLine.slang new file mode 100644 index 00000000..357517fb --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/NoFirstLine.slang @@ -0,0 +1,10 @@ + +// copyright 2018 + +if (cond) { + a = b + 1 +}; + +x = 4; +x; +// Noncompliant@0 {{Add or update the header of this file.}} \ No newline at end of file diff --git a/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Noncompliant.slang b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Noncompliant.slang new file mode 100644 index 00000000..2570b181 --- /dev/null +++ b/slang-checks/src/test/resources/org/sonarsource/slang/checks/fileheader/Noncompliant.slang @@ -0,0 +1,9 @@ +// Noncompliant@0 {{Add or update the header of this file.}} +// comment + +if (cond) { + a = b + 1 +}; + +x = 4; +x; \ No newline at end of file diff --git a/slang-plugin/build.gradle b/slang-plugin/build.gradle new file mode 100644 index 00000000..a3d8fb99 --- /dev/null +++ b/slang-plugin/build.gradle @@ -0,0 +1,15 @@ +dependencies { + implementation project(':slang-api') + implementation project(':slang-checks') + implementation 'org.sonarsource.analyzer-commons:sonar-analyzer-commons' + compileOnly 'org.sonarsource.api.plugin:sonar-plugin-api' + testImplementation 'org.sonarsource.sonarqube:sonar-plugin-api-impl' + testImplementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures' + implementation 'com.google.code.findbugs:jsr305' + testImplementation project(':slang-antlr') + testImplementation project(':slang-testing') + testImplementation "org.junit.jupiter:junit-jupiter-api" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.mockito:mockito-core' +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/AbstractPropertyHandlerSensor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/AbstractPropertyHandlerSensor.java new file mode 100644 index 00000000..384b8aa1 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/AbstractPropertyHandlerSensor.java @@ -0,0 +1,110 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.File; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonarsource.analyzer.commons.ExternalReportProvider; + +public abstract class AbstractPropertyHandlerSensor implements Sensor { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractPropertyHandlerSensor.class); + private final AnalysisWarnings analysisWarnings; + private final String propertyKey; + private final String propertyName; + private final String configurationKey; + private final String languageKey; + + protected AbstractPropertyHandlerSensor(AnalysisWarnings analysisWarnings, String propertyKey, String propertyName, + String configurationKey, String languageKey) { + this.analysisWarnings = analysisWarnings; + this.propertyKey = propertyKey; + this.propertyName = propertyName; + this.configurationKey = configurationKey; + this.languageKey = languageKey; + } + + public final String propertyName() { + return propertyName; + } + + public final String propertyKey() { + return propertyKey; + } + + public final String configurationKey() { + return configurationKey; + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .onlyOnLanguage(languageKey) + .onlyWhenConfiguration(conf -> conf.hasKey(configurationKey())) + .name("Import of " + propertyName() + " issues"); + } + + @Override + public void execute(SensorContext context) { + executeOnFiles(reportFiles(context), reportConsumer(context)); + } + + public abstract Consumer reportConsumer(SensorContext context); + + private void executeOnFiles(List reportFiles, Consumer action) { + reportFiles.stream() + .filter(File::exists) + .forEach(file -> { + LOG.info("Importing {}", file); + action.accept(file); + }); + reportMissingFiles(reportFiles); + } + + private List reportFiles(SensorContext context) { + return ExternalReportProvider.getReportFiles(context, configurationKey()); + } + + private void reportMissingFiles(List reportFiles) { + List missingFiles = reportFiles.stream() + .filter(file -> !file.exists()) + .map(File::getPath) + .collect(Collectors.toList()); + + if (!missingFiles.isEmpty()) { + String missingFilesAsString = missingFiles.stream().collect(Collectors.joining("\n- ", "\n- ", "")); + String logWarning = String.format("Unable to import %s report file(s):%s%nThe report file(s) can not be found. Check that the property '%s' is correctly configured.", + propertyName(), missingFilesAsString, configurationKey()); + LOG.warn(logWarning); + + String uiWarning = String.format("Unable to import %d %s report file(s).%nPlease check that property '%s' is correctly configured and the analysis logs for more details.", + missingFiles.size(), propertyName(), configurationKey()); + analysisWarnings.addUnique(uiWarning); + } + } +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/ChecksVisitor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/ChecksVisitor.java new file mode 100644 index 00000000..7f16fce9 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/ChecksVisitor.java @@ -0,0 +1,132 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.Objects; +import java.util.function.BiConsumer; +import javax.annotation.Nullable; +import org.sonar.api.batch.rule.Checks; +import org.sonar.api.rule.RuleKey; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.visitors.TreeVisitor; + +public class ChecksVisitor extends TreeVisitor { + + private final DurationStatistics statistics; + + public ChecksVisitor(Checks checks, DurationStatistics statistics) { + this.statistics = statistics; + Collection rulesActiveInSonarQube = checks.all(); + for (SlangCheck check : rulesActiveInSonarQube) { + RuleKey ruleKey = checks.ruleKey(check); + Objects.requireNonNull(ruleKey); + check.initialize(new ContextAdapter(ruleKey)); + } + } + + public class ContextAdapter implements InitContext, CheckContext { + + public final RuleKey ruleKey; + private InputFileContext currentCtx; + + public ContextAdapter(RuleKey ruleKey) { + this.ruleKey = ruleKey; + } + + @Override + public void register(Class cls, BiConsumer visitor) { + ChecksVisitor.this.register(cls, statistics.time(ruleKey.rule(), (ctx, tree) -> { + currentCtx = ctx; + visitor.accept(this, tree); + })); + } + + @Override + public Deque ancestors() { + return currentCtx.ancestors(); + } + + @Override + public String filename() { + return currentCtx.inputFile.filename(); + } + + @Override + public String fileContent() { + try { + return currentCtx.inputFile.contents(); + } catch (IOException e) { + throw new IllegalStateException("Cannot read content of " + currentCtx.inputFile, e); + } + } + + @Override + public void reportIssue(TextRange textRange, String message) { + reportIssue(textRange, message, Collections.emptyList(), null); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message) { + reportIssue(toHighlight, message, Collections.emptyList()); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message, SecondaryLocation secondaryLocation) { + reportIssue(toHighlight, message, Collections.singletonList(secondaryLocation)); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations) { + reportIssue(toHighlight, message, secondaryLocations, null); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations, @Nullable Double gap) { + reportIssue(toHighlight.textRange(), message, secondaryLocations, gap); + } + + @Override + public void reportFileIssue(String message) { + reportFileIssue(message, null); + } + + @Override + public void reportFileIssue(String message, @Nullable Double gap) { + reportIssue((TextRange) null, message, Collections.emptyList(), gap); + } + + private void reportIssue(@Nullable TextRange textRange, String message, List secondaryLocations, @Nullable Double gap) { + currentCtx.reportIssue(ruleKey, textRange, message, secondaryLocations, gap); + } + + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CommentAnalysisUtils.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CommentAnalysisUtils.java new file mode 100644 index 00000000..e427dfef --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CommentAnalysisUtils.java @@ -0,0 +1,73 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.TextRange; + +public class CommentAnalysisUtils { + public static final String NOSONAR_PREFIX = "NOSONAR"; + + private static final boolean[] IS_NON_BLANK_CHAR_IN_COMMENTS = new boolean[127]; + static { + for (int c = 0; c < IS_NON_BLANK_CHAR_IN_COMMENTS.length; c++) { + IS_NON_BLANK_CHAR_IN_COMMENTS[c] = c > ' ' && "*#-=|".indexOf(c) == -1; + } + } + + private CommentAnalysisUtils() { } + + static boolean isNosonarComment(Comment comment) { + return comment.contentText().trim().toUpperCase(Locale.ENGLISH).startsWith(NOSONAR_PREFIX); + } + + static Set findNonEmptyCommentLines(TextRange range, String content) { + Set lineNumbers = new HashSet<>(); + + int startLine = range.start().line(); + if (startLine == range.end().line()) { + if (isNotBlank(content)) { + lineNumbers.add(startLine); + } + } else { + String[] lines = content.split("\r\n|\n|\r", -1); + for (int i = 0; i < lines.length; i++) { + if (isNotBlank(lines[i])) { + lineNumbers.add(startLine + i); + } + } + } + + return lineNumbers; + } + + private static boolean isNotBlank(String line) { + for (int i = 0; i < line.length(); i++) { + char ch = line.charAt(i); + if (ch >= IS_NON_BLANK_CHAR_IN_COMMENTS.length || IS_NON_BLANK_CHAR_IN_COMMENTS[ch]) { + return true; + } + } + return false; + } +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CpdVisitor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CpdVisitor.java new file mode 100644 index 00000000..a1605e70 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CpdVisitor.java @@ -0,0 +1,58 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.List; +import org.sonar.api.batch.sensor.cpd.NewCpdTokens; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.visitors.TreeVisitor; + +public class CpdVisitor extends TreeVisitor { + + private NewCpdTokens cpdTokens; + + public CpdVisitor() { + register(TopLevelTree.class, (ctx, tree) -> { + List tokens = tree.metaData().tokens(); + + boolean foundFirstToken = (tree.firstCpdToken() == null); + + for (Token token : tokens) { + foundFirstToken = foundFirstToken || (token == tree.firstCpdToken()); + if (foundFirstToken) { + String text = token.type() == Token.Type.STRING_LITERAL ? "LITERAL" : token.text(); + cpdTokens.addToken(ctx.textRange(token.textRange()), text); + } + } + }); + } + + @Override + protected void before(InputFileContext ctx, Tree root) { + cpdTokens = ctx.sensorContext.newCpdTokens().onFile(ctx.inputFile); + } + + @Override + protected void after(InputFileContext ctx, Tree root) { + cpdTokens.save(); + } +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CyclomaticComplexityVisitor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CyclomaticComplexityVisitor.java new file mode 100644 index 00000000..1f689f0c --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/CyclomaticComplexityVisitor.java @@ -0,0 +1,74 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.IfTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.visitors.TreeContext; +import org.sonarsource.slang.visitors.TreeVisitor; +import java.util.ArrayList; +import java.util.List; + +public class CyclomaticComplexityVisitor extends TreeVisitor { + + private List complexityTrees = new ArrayList<>(); + + public CyclomaticComplexityVisitor() { + + register(FunctionDeclarationTree.class, (ctx, tree) -> { + if (tree.name() != null && tree.body() != null) { + complexityTrees.add(tree); + } + }); + + register(IfTree.class, (ctx, tree) -> complexityTrees.add(tree.ifKeyword())); + + register(LoopTree.class, (ctx, tree) -> complexityTrees.add(tree)); + + register(MatchCaseTree.class, (ctx, tree) -> { + if (tree.expression() != null) { + complexityTrees.add(tree); + } + }); + + register(BinaryExpressionTree.class, (ctx, tree) -> { + if (tree.operator() == BinaryExpressionTree.Operator.CONDITIONAL_AND || + tree.operator() == BinaryExpressionTree.Operator.CONDITIONAL_OR) { + complexityTrees.add(tree); + } + }); + } + + public List complexityTrees(Tree tree) { + this.complexityTrees = new ArrayList<>(); + this.scan(new TreeContext(), tree); + return this.complexityTrees; + } + + @Override + protected void before(TreeContext ctx, Tree root) { + complexityTrees = new ArrayList<>(); + } +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/DurationStatistics.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/DurationStatistics.java new file mode 100644 index 00000000..f95346d8 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/DurationStatistics.java @@ -0,0 +1,101 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.text.NumberFormat; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiConsumer; +import java.util.function.Supplier; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.config.Configuration; + +class DurationStatistics { + + private static final Logger LOG = LoggerFactory.getLogger(DurationStatistics.class); + + private static final String PROPERTY_KEY = "sonar.slang.duration.statistics"; + + private final Map stats = new ConcurrentHashMap<>(); + + private final boolean recordStat; + + DurationStatistics(Configuration config) { + recordStat = config.getBoolean(PROPERTY_KEY).orElse(false); + } + + BiConsumer time(String id, BiConsumer consumer) { + if (recordStat) { + return (t, u) -> time(id, () -> consumer.accept(t, u)); + } else { + return consumer; + } + } + + void time(String id, Runnable runnable) { + if (recordStat) { + time(id, () -> { + runnable.run(); + return null; + }); + } else { + runnable.run(); + } + } + + T time(String id, Supplier supplier) { + if (recordStat) { + long startTime = System.nanoTime(); + T result = supplier.get(); + record(id, System.nanoTime() - startTime); + return result; + } else { + return supplier.get(); + } + } + + void record(String id, long elapsedTime) { + stats.computeIfAbsent(id, key -> new AtomicLong(0)).addAndGet(elapsedTime); + } + + void log() { + if (recordStat) { + StringBuilder out = new StringBuilder(); + DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.ROOT); + symbols.setGroupingSeparator('\''); + NumberFormat format = new DecimalFormat("#,###", symbols); + out.append("Duration Statistics"); + stats.entrySet().stream() + .sorted((a, b) -> Long.compare(b.getValue().get(), a.getValue().get())) + .forEach(e -> out.append(", ") + .append(e.getKey()) + .append(" ") + .append(format.format(e.getValue().get() / 1_000_000L)) + .append(" ms")); + LOG.info("{}", out); + } + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/InputFileContext.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/InputFileContext.java new file mode 100644 index 00000000..c41a885e --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/InputFileContext.java @@ -0,0 +1,137 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import javax.annotation.Nullable; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextPointer; +import org.sonar.api.batch.fs.TextRange; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.error.NewAnalysisError; +import org.sonar.api.batch.sensor.issue.NewIssue; +import org.sonar.api.batch.sensor.issue.NewIssueLocation; +import org.sonar.api.rule.RuleKey; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.visitors.TreeContext; + +public class InputFileContext extends TreeContext { + + private static final String PARSING_ERROR_RULE_KEY = "ParsingError"; + private Map> filteredRules = new HashMap<>(); + + public final SensorContext sensorContext; + + public final InputFile inputFile; + + public InputFileContext(SensorContext sensorContext, InputFile inputFile) { + this.sensorContext = sensorContext; + this.inputFile = inputFile; + } + + public TextRange textRange(org.sonarsource.slang.api.TextRange textRange) { + return inputFile.newRange( + textRange.start().line(), + textRange.start().lineOffset(), + textRange.end().line(), + textRange.end().lineOffset()); + } + + public void reportIssue(RuleKey ruleKey, + @Nullable org.sonarsource.slang.api.TextRange textRange, + String message, + List secondaryLocations, + @Nullable Double gap) { + + if (textRange != null && filteredRules.getOrDefault(ruleKey.toString(), Collections.emptySet()) + .stream().anyMatch(textRange::isInside)) { + // Issue is filtered by one of the filter. + return; + } + + NewIssue issue = sensorContext.newIssue(); + NewIssueLocation issueLocation = issue.newLocation() + .on(inputFile) + .message(message); + + if (textRange != null) { + issueLocation.at(textRange(textRange)); + } + + issue + .forRule(ruleKey) + .at(issueLocation) + .gap(gap); + + secondaryLocations.forEach(secondary -> issue.addLocation( + issue.newLocation() + .on(inputFile) + .at(textRange(secondary.textRange)) + .message(secondary.message == null ? "" : secondary.message))); + + issue.save(); + } + + public void reportAnalysisParseError(String repositoryKey, InputFile inputFile, @Nullable org.sonarsource.slang.api.TextPointer location) { + reportAnalysisError("Unable to parse file: " + inputFile, location); + RuleKey parsingErrorRuleKey = RuleKey.of(repositoryKey, PARSING_ERROR_RULE_KEY); + if (sensorContext.activeRules().find(parsingErrorRuleKey) == null) { + return; + } + NewIssue parseError = sensorContext.newIssue(); + NewIssueLocation parseErrorLocation = parseError.newLocation() + .on(inputFile) + .message("A parsing error occurred in this file."); + + Optional.ofNullable(location) + .map(org.sonarsource.slang.api.TextPointer::line) + .map(inputFile::selectLine) + .ifPresent(parseErrorLocation::at); + + parseError + .forRule(parsingErrorRuleKey) + .at(parseErrorLocation) + .save(); + } + + public void reportAnalysisError(String message, @Nullable org.sonarsource.slang.api.TextPointer location) { + NewAnalysisError error = sensorContext.newAnalysisError(); + error + .message(message) + .onFile(inputFile); + + if (location != null) { + TextPointer pointerLocation = inputFile.newPointer(location.line(), location.lineOffset()); + error.at(pointerLocation); + } + + error.save(); + } + + public void setFilteredRules(Map> filteredRules) { + this.filteredRules = filteredRules; + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/IssueSuppressionVisitor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/IssueSuppressionVisitor.java new file mode 100644 index 00000000..6a74d47d --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/IssueSuppressionVisitor.java @@ -0,0 +1,96 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.visitors.TreeVisitor; + +public class IssueSuppressionVisitor extends TreeVisitor { + + private Map> filteredRules; + + private static final List SUPPRESS_ANNOTATION_NAMES = Arrays.asList("Suppress", "SuppressWarnings"); + + private static final Pattern LITERAL_PATTERN = Pattern.compile("\"(.*?)\""); + + public IssueSuppressionVisitor() { + register(FunctionDeclarationTree.class, (ctx, tree) -> checkSuppressAnnotations(tree)); + register(ClassDeclarationTree.class, (ctx, tree) -> checkSuppressAnnotations(tree)); + register(VariableDeclarationTree.class, (ctx, tree) -> checkSuppressAnnotations(tree)); + register(ParameterTree.class, (ctx, tree) -> checkSuppressAnnotations(tree)); + } + + private void checkSuppressAnnotations(Tree tree) { + List annotations = tree.metaData().annotations(); + TextRange textRange = tree.textRange(); + + annotations.forEach(annotation -> { + if (SUPPRESS_ANNOTATION_NAMES.contains(annotation.shortName())) { + getSuppressedKeys(annotation.argumentsText()).forEach(ruleKey -> + filteredRules.computeIfAbsent(ruleKey, key -> new HashSet<>()).add(textRange) + ); + } + }); + } + + private static Collection getSuppressedKeys(List argumentsText) { + List keys = new ArrayList<>(); + for (String s : argumentsText) { + keys.addAll(getArgumentsValues(s)); + } + return keys; + } + + private static Collection getArgumentsValues(String argumentText) { + List values = new ArrayList<>(); + Matcher m = LITERAL_PATTERN.matcher(argumentText); + while (m.find()) { + values.add(m.group(1)); + } + return values; + } + + @Override + protected void before(InputFileContext ctx, Tree root) { + filteredRules = new HashMap<>(); + } + + @Override + protected void after(InputFileContext ctx, Tree root) { + ctx.setFilteredRules(filteredRules); + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/MetricVisitor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/MetricVisitor.java new file mode 100644 index 00000000..503ae84d --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/MetricVisitor.java @@ -0,0 +1,155 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; +import org.sonar.api.batch.measure.Metric; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.complexity.CognitiveComplexity; +import org.sonarsource.slang.visitors.TreeVisitor; + +public class MetricVisitor extends TreeVisitor { + private final FileLinesContextFactory fileLinesContextFactory; + private final Predicate executableLineOfCodePredicate; + + private Set linesOfCode; + private Set commentLines; + private Set executableLines; + private int numberOfFunctions; + private int numberOfClasses; + private int complexity; + private int statements; + private int cognitiveComplexity; + + public MetricVisitor(FileLinesContextFactory fileLinesContextFactory, Predicate executableLineOfCodePredicate) { + this.fileLinesContextFactory = fileLinesContextFactory; + this.executableLineOfCodePredicate = executableLineOfCodePredicate; + + register(TopLevelTree.class, (ctx, tree) -> { + List declarations = tree.declarations(); + int firstTokenLine = declarations.isEmpty() ? tree.textRange().end().line() : declarations.get(0).textRange().start().line(); + tree.allComments() + .forEach(comment -> commentLines.addAll(findNonEmptyCommentLines(comment, firstTokenLine))); + addExecutableLines(declarations); + linesOfCode.addAll(tree.metaData().linesOfCode()); + complexity = new CyclomaticComplexityVisitor().complexityTrees(tree).size(); + statements = new StatementsVisitor().statements(tree); + cognitiveComplexity = new CognitiveComplexity(tree).value(); + }); + + register(FunctionDeclarationTree.class, (ctx, tree) -> { + if (tree.name() != null && tree.body() != null) { + numberOfFunctions++; + } + }); + + register(ClassDeclarationTree.class, (ctx, tree) -> numberOfClasses++); + + register(BlockTree.class, (ctx, tree) -> addExecutableLines(tree.statementOrExpressions())); + } + + static Set findNonEmptyCommentLines(Comment comment, int firstTokenLine) { + boolean isFileHeader = comment.textRange().end().line() < firstTokenLine; + + if (!isFileHeader && ! CommentAnalysisUtils.isNosonarComment(comment)) { + return CommentAnalysisUtils.findNonEmptyCommentLines(comment.contentRange(), comment.contentText()); + } + + return Set.of(); + } + + private void addExecutableLines(List trees) { + trees.stream() + .filter(executableLineOfCodePredicate) + .forEach(t -> executableLines.add(t.metaData().textRange().start().line())); + } + + @Override + protected void before(InputFileContext ctx, Tree root) { + linesOfCode = new HashSet<>(); + commentLines = new HashSet<>(); + executableLines = new HashSet<>(); + numberOfFunctions = 0; + numberOfClasses = 0; + complexity = 0; + cognitiveComplexity = 0; + } + + @Override + protected void after(InputFileContext ctx, Tree root) { + saveMetric(ctx, CoreMetrics.NCLOC, linesOfCode().size()); + saveMetric(ctx, CoreMetrics.COMMENT_LINES, commentLines().size()); + saveMetric(ctx, CoreMetrics.FUNCTIONS, numberOfFunctions()); + saveMetric(ctx, CoreMetrics.CLASSES, numberOfClasses()); + saveMetric(ctx, CoreMetrics.COMPLEXITY, complexity); + saveMetric(ctx, CoreMetrics.STATEMENTS, statements); + saveMetric(ctx, CoreMetrics.COGNITIVE_COMPLEXITY, cognitiveComplexity); + + FileLinesContext fileLinesContext = fileLinesContextFactory.createFor(ctx.inputFile); + linesOfCode().forEach(line -> fileLinesContext.setIntValue(CoreMetrics.NCLOC_DATA_KEY, line, 1)); + executableLines().forEach(line -> fileLinesContext.setIntValue(CoreMetrics.EXECUTABLE_LINES_DATA_KEY, line, 1)); + fileLinesContext.save(); + } + + private static void saveMetric(InputFileContext ctx, Metric metric, Integer value) { + ctx.sensorContext.newMeasure() + .on(ctx.inputFile) + .forMetric(metric) + .withValue(value) + .save(); + } + + + public Set linesOfCode() { + return linesOfCode; + } + + public Set commentLines() { + return commentLines; + } + + public Set executableLines() { + return executableLines; + } + + public int numberOfFunctions() { + return numberOfFunctions; + } + + public int numberOfClasses() { + return numberOfClasses; + } + + public int cognitiveComplexity() { + return cognitiveComplexity; + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/RulesDefinitionUtils.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/RulesDefinitionUtils.java new file mode 100644 index 00000000..2108235e --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/RulesDefinitionUtils.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.utils.AnnotationUtils; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; +import org.sonarsource.slang.checks.utils.PropertyDefaultValues; + +public class RulesDefinitionUtils { + + private RulesDefinitionUtils() { + } + + public static void setDefaultValuesForParameters(RulesDefinition.NewRepository repository, List> checks, Language language) { + for (Class check : checks) { + org.sonar.check.Rule ruleAnnotation = AnnotationUtils.getAnnotation(check, org.sonar.check.Rule.class); + String ruleKey = ruleAnnotation.key(); + for (Field field : check.getDeclaredFields()) { + RuleProperty ruleProperty = field.getAnnotation(RuleProperty.class); + PropertyDefaultValues defaultValues = field.getAnnotation(PropertyDefaultValues.class); + if (ruleProperty == null || defaultValues == null) { + continue; + } + String paramKey = ruleProperty.key(); + + List valueForLanguage = Arrays.stream(defaultValues.value()) + .filter(defaultValue -> defaultValue.language() == language) + .collect(Collectors.toList()); + if (valueForLanguage.size() != 1) { + throw new IllegalStateException("Invalid @PropertyDefaultValue on " + check.getSimpleName() + + " for language " + language); + } + valueForLanguage + .forEach(defaultValue -> repository.rule(ruleKey).param(paramKey).setDefaultValue(defaultValue.defaultValue())); + } + } + } +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SkipNoSonarLinesVisitor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SkipNoSonarLinesVisitor.java new file mode 100644 index 00000000..65a2d8a4 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SkipNoSonarLinesVisitor.java @@ -0,0 +1,66 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.sonar.api.issue.NoSonarFilter; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.visitors.TreeVisitor; + +public class SkipNoSonarLinesVisitor extends TreeVisitor { + + private final NoSonarFilter noSonarFilter; + + private Set noSonarLines; + public SkipNoSonarLinesVisitor(NoSonarFilter noSonarFilter) { + this.noSonarFilter = noSonarFilter; + + register(TopLevelTree.class, (ctx, tree) -> { + List declarations = tree.declarations(); + int firstTokenLine = declarations.isEmpty() ? tree.textRange().end().line() : declarations.get(0).textRange().start().line(); + tree.allComments() + .forEach(comment -> noSonarLines.addAll(findNoSonarCommentLines(comment, firstTokenLine))); + }); + } + + @Override + protected void before(InputFileContext ctx, Tree root) { + noSonarLines = new HashSet<>(); + } + + @Override + protected void after(InputFileContext ctx, Tree root) { + noSonarFilter.noSonarInFile(ctx.inputFile, noSonarLines); + } + + private static Set findNoSonarCommentLines(Comment comment, int firstTokenLine) { + boolean isFileHeader = comment.textRange().end().line() < firstTokenLine; + + if (!isFileHeader && CommentAnalysisUtils.isNosonarComment(comment)) { + return CommentAnalysisUtils.findNonEmptyCommentLines(comment.contentRange(), comment.contentText()); + } + + return Set.of(); + } +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SlangSensor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SlangSensor.java new file mode 100644 index 00000000..98e62375 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SlangSensor.java @@ -0,0 +1,234 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.IOException; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.function.Predicate; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.SonarProduct; +import org.sonar.api.SonarRuntime; +import org.sonar.api.batch.fs.FilePredicate; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.Checks; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.issue.NoSonarFilter; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonar.api.resources.Language; +import org.sonar.api.utils.Version; +import org.sonarsource.analyzer.commons.ProgressReport; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.ImportDeclarationTree; +import org.sonarsource.slang.api.PackageDeclarationTree; +import org.sonarsource.slang.api.ParseException; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.plugin.converter.ASTConverterValidation; +import org.sonarsource.slang.visitors.TreeVisitor; + +public abstract class SlangSensor implements Sensor { + // VisibleForTesting + static final Predicate EXECUTABLE_LINE_PREDICATE = t -> + !(t instanceof PackageDeclarationTree) + && !(t instanceof ImportDeclarationTree) + && !(t instanceof ClassDeclarationTree) + && !(t instanceof FunctionDeclarationTree) + && !(t instanceof BlockTree); + + private static final Logger LOG = LoggerFactory.getLogger(SlangSensor.class); + private static final Pattern EMPTY_FILE_CONTENT_PATTERN = Pattern.compile("\\s*+"); + + protected final SonarRuntime sonarRuntime; + private final NoSonarFilter noSonarFilter; + private final Language language; + private FileLinesContextFactory fileLinesContextFactory; + + protected SlangSensor(SonarRuntime sonarRuntime, NoSonarFilter noSonarFilter, FileLinesContextFactory fileLinesContextFactory, Language language) { + this.sonarRuntime = sonarRuntime; + this.noSonarFilter = noSonarFilter; + this.fileLinesContextFactory = fileLinesContextFactory; + this.language = language; + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .onlyOnLanguage(language.getKey()) + .name(language.getName() + " Sensor"); + processesFilesIndependently(descriptor); + } + + protected void processesFilesIndependently(SensorDescriptor descriptor) { + if ((sonarRuntime.getProduct() == SonarProduct.SONARLINT) + || !sonarRuntime.getApiVersion().isGreaterThanOrEqual(Version.create(9, 3))) { + return; + } + try { + Method method = descriptor.getClass().getMethod("processesFilesIndependently"); + method.invoke(descriptor); + } catch (ReflectiveOperationException e) { + LOG.warn("Could not call SensorDescriptor.processesFilesIndependently() method", e); + } + } + + protected abstract ASTConverter astConverter(SensorContext sensorContext); + + protected abstract Checks checks(); + + protected abstract String repositoryKey(); + + protected Predicate executableLineOfCodePredicate() { + return EXECUTABLE_LINE_PREDICATE; + } + + private boolean analyseFiles(ASTConverter converter, + SensorContext sensorContext, + Iterable inputFiles, + ProgressReport progressReport, + List> visitors, + DurationStatistics statistics) { + + for (InputFile inputFile : inputFiles) { + if (sensorContext.isCancelled()) { + return false; + } + InputFileContext inputFileContext = new InputFileContext(sensorContext, inputFile); + try { + analyseFile(converter, inputFileContext, inputFile, visitors, statistics); + } catch (ParseException e) { + logParsingError(inputFile, e); + inputFileContext.reportAnalysisParseError(repositoryKey(), inputFile, e.getPosition()); + } + progressReport.nextFile(); + } + return true; + } + + private static void analyseFile(ASTConverter converter, + InputFileContext inputFileContext, + InputFile inputFile, + List> visitors, + DurationStatistics statistics) { + String content; + String fileName; + try { + content = inputFile.contents(); + fileName = inputFile.toString(); + } catch (IOException | RuntimeException e) { + throw toParseException("read", inputFile, e); + } + + if (EMPTY_FILE_CONTENT_PATTERN.matcher(content).matches()) { + return; + } + + Tree tree = statistics.time("Parse", () -> { + try { + return converter.parse(content, fileName); + } catch (RuntimeException e) { + throw toParseException("parse", inputFile, e); + } + }); + for (TreeVisitor visitor : visitors) { + try { + String visitorId = visitor.getClass().getSimpleName(); + statistics.time(visitorId, () -> visitor.scan(inputFileContext, tree)); + } catch (RuntimeException e) { + inputFileContext.reportAnalysisError(e.getMessage(), null); + LOG.error("Cannot analyse '" + inputFile +"': " + e.getMessage(), e); + } + } + } + + private static ParseException toParseException(String action, InputFile inputFile, Exception cause) { + TextPointer position = cause instanceof ParseException ? ((ParseException) cause).getPosition() : null; + return new ParseException("Cannot " + action + " '" + inputFile + "': " + cause.getMessage(), position, cause); + } + + private static void logParsingError(InputFile inputFile, ParseException e) { + TextPointer position = e.getPosition(); + String positionMessage = ""; + if (position != null) { + positionMessage = String.format("Parse error at position %s:%s", position.line(), position.lineOffset()); + } + LOG.error("Unable to parse file: {}. {}", inputFile.uri(), positionMessage); + LOG.error(e.getMessage()); + } + + @Override + public void execute(SensorContext sensorContext) { + DurationStatistics statistics = new DurationStatistics(sensorContext.config()); + FileSystem fileSystem = sensorContext.fileSystem(); + FilePredicate mainFilePredicate = fileSystem.predicates().and( + fileSystem.predicates().hasLanguage(language.getKey()), + fileSystem.predicates().hasType(InputFile.Type.MAIN)); + Iterable inputFiles = fileSystem.inputFiles(mainFilePredicate); + List filenames = StreamSupport.stream(inputFiles.spliterator(), false).map(InputFile::toString).collect(Collectors.toList()); + ProgressReport progressReport = new ProgressReport("Progress of the " + language.getName() + " analysis", TimeUnit.SECONDS.toMillis(10)); + progressReport.start(filenames); + boolean success = false; + ASTConverter converter = ASTConverterValidation.wrap(astConverter(sensorContext), sensorContext.config()); + try { + success = analyseFiles(converter, sensorContext, inputFiles, progressReport, visitors(sensorContext, statistics), statistics); + } finally { + if (success) { + progressReport.stop(); + } else { + progressReport.cancel(); + } + converter.terminate(); + } + statistics.log(); + } + + private List> visitors(SensorContext sensorContext, DurationStatistics statistics) { + if (sensorContext.runtime().getProduct() == SonarProduct.SONARLINT) { + return Arrays.asList( + new IssueSuppressionVisitor(), + new SkipNoSonarLinesVisitor(noSonarFilter), + new ChecksVisitor(checks(), statistics) + ); + } else { + return Arrays.asList( + new IssueSuppressionVisitor(), + new MetricVisitor(fileLinesContextFactory, executableLineOfCodePredicate()), + new SkipNoSonarLinesVisitor(noSonarFilter), + new ChecksVisitor(checks(), statistics), + new CpdVisitor(), + new SyntaxHighlighter() + ); + } + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/StatementsVisitor.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/StatementsVisitor.java new file mode 100644 index 00000000..66973521 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/StatementsVisitor.java @@ -0,0 +1,80 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.ImportDeclarationTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.PackageDeclarationTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.visitors.TreeContext; +import org.sonarsource.slang.visitors.TreeVisitor; + +public class StatementsVisitor extends TreeVisitor { + + private int statements; + + public StatementsVisitor() { + + register(BlockTree.class, (ctx, tree) -> + tree.statementOrExpressions().forEach(stmt -> { + if (!isDeclaration(stmt)) { + statements++; + } + })); + + register(TopLevelTree.class, (ctx, tree) -> + tree.declarations().forEach(decl -> { + if (!isDeclaration(decl) && !isNative(decl) && !isBlock(decl)) { + statements++; + } + })); + } + + public int statements(Tree tree) { + statements = 0; + scan(new TreeContext(), tree); + return statements; + } + + @Override + protected void before(TreeContext ctx, Tree root) { + statements = 0; + } + + private static boolean isDeclaration(Tree tree) { + return tree instanceof ClassDeclarationTree + || tree instanceof FunctionDeclarationTree + || tree instanceof PackageDeclarationTree + || tree instanceof ImportDeclarationTree; + } + + private static boolean isNative(Tree tree) { + return tree instanceof NativeTree; + } + + private static boolean isBlock(Tree tree) { + return tree instanceof BlockTree; + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SyntaxHighlighter.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SyntaxHighlighter.java new file mode 100644 index 00000000..871941a0 --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/SyntaxHighlighter.java @@ -0,0 +1,69 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import org.sonar.api.batch.sensor.highlighting.NewHighlighting; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.visitors.TreeVisitor; + +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.COMMENT; +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.CONSTANT; +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD; +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.STRING; + +public class SyntaxHighlighter extends TreeVisitor { + + private NewHighlighting newHighlighting; + + public SyntaxHighlighter() { + register(TopLevelTree.class, (ctx, tree) -> { + tree.allComments().forEach( + comment -> highlight(ctx, comment.textRange(), COMMENT)); + tree.metaData().tokens().stream() + .filter(t -> t.type() == Token.Type.KEYWORD) + .forEach(token -> highlight(ctx, token.textRange(), KEYWORD)); + }); + + register(LiteralTree.class, (ctx, tree) -> + highlight(ctx, tree.metaData().textRange(), tree instanceof StringLiteralTree ? STRING : CONSTANT)); + } + + @Override + protected void before(InputFileContext ctx, Tree root) { + newHighlighting = ctx.sensorContext.newHighlighting() + .onFile(ctx.inputFile); + } + + @Override + protected void after(InputFileContext ctx, Tree root) { + newHighlighting.save(); + } + + private void highlight(InputFileContext ctx, TextRange range, TypeOfText typeOfText) { + newHighlighting.highlight(ctx.textRange(range), typeOfText); + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/converter/ASTConverterValidation.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/converter/ASTConverterValidation.java new file mode 100644 index 00000000..846736cb --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/converter/ASTConverterValidation.java @@ -0,0 +1,343 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin.converter; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.config.Configuration; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.PlaceHolderTreeImpl; +import org.sonarsource.slang.impl.TextPointerImpl; + +import static org.sonarsource.slang.utils.LogArg.lazyArg; + +public class ASTConverterValidation implements ASTConverter { + + private static final Logger LOG = LoggerFactory.getLogger(ASTConverterValidation.class); + + private static final Pattern PUNCTUATOR_PATTERN = Pattern.compile("[^0-9A-Za-z]++"); + + private static final Set ALLOWED_MISPLACED_TOKENS_OUTSIDE_PARENT_RANGE = new HashSet<>(Collections.singleton("implicit")); + + private final ASTConverter wrapped; + + private final Map firstErrorOfEachKind = new TreeMap<>(); + + private final ValidationMode mode; + + public enum ValidationMode { + THROW_EXCEPTION, + LOG_ERROR + } + + @Nullable + private String currentFile = null; + + public ASTConverterValidation(ASTConverter wrapped, ValidationMode mode) { + this.wrapped = wrapped; + this.mode = mode; + } + + public static ASTConverter wrap(ASTConverter converter, Configuration configuration) { + String mode = configuration.get("sonar.slang.converter.validation").orElse(null); + if (mode == null) { + return converter; + } else if (mode.equals("throw")) { + return new ASTConverterValidation(converter, ValidationMode.THROW_EXCEPTION); + } else if (mode.equals("log")) { + return new ASTConverterValidation(converter, ValidationMode.LOG_ERROR); + } else { + throw new IllegalStateException("Unsupported mode: " + mode); + } + } + + @Override + public Tree parse(String content) { + return parse(content, null); + } + + @Override + public Tree parse(String content, @Nullable String currentFile) { + this.currentFile = currentFile; + Tree tree = wrapped.parse(content, currentFile); + assertTreeIsValid(tree); + assertTokensMatchSourceCode(tree, content); + return tree; + } + + @Override + public void terminate() { + List errors = errors(); + if (!errors.isEmpty()) { + String delimiter = "\n [AST ERROR] "; + LOG.error("AST Converter Validation detected {} errors:{}{}", errors.size(), delimiter, lazyArg(() -> String.join(delimiter, errors))); + } + wrapped.terminate(); + } + + // VisibleForTesting + ValidationMode mode() { + return mode; + } + + // VisibleForTesting + List errors() { + return firstErrorOfEachKind.entrySet().stream() + .map(entry -> entry.getKey() + entry.getValue()) + .collect(Collectors.toList()); + } + + private void raiseError(String messageKey, String messageDetails, TextPointer position) { + if (mode == ValidationMode.THROW_EXCEPTION) { + throw new IllegalStateException("ASTConverterValidationException: " + messageKey + messageDetails + + " at " + position.line() + ":" + position.lineOffset()); + } else { + String positionDetails = String.format(" (line: %d, column: %d)", position.line(), (position.lineOffset() + 1)); + if (currentFile != null) { + positionDetails += " in file: " + currentFile; + } + firstErrorOfEachKind.putIfAbsent(messageKey, messageDetails + positionDetails); + } + } + + private static String kind(Tree tree) { + return tree.getClass().getSimpleName(); + } + + private void assertTreeIsValid(Tree tree) { + assertTextRangeIsValid(tree); + assertTreeHasAtLeastOneToken(tree); + assertTokensAndChildTokens(tree); + for (Tree child : tree.children()) { + if (child == null) { + raiseError(kind(tree) + " has a null child", "", tree.textRange().start()); + } else if (child.metaData() == null) { + raiseError(kind(child) + " metaData is null", "", tree.textRange().start()); + } else { + assertTreeIsValid(child); + } + } + } + + private void assertTextRangeIsValid(Tree tree) { + TextPointer start = tree.metaData().textRange().start(); + TextPointer end = tree.metaData().textRange().end(); + + boolean startOffsetAfterEndOffset = !(tree instanceof TopLevelTree) && + start.line() == end.line() && + start.lineOffset() >= end.lineOffset(); + + if (start.line() <= 0 || end.line() <= 0 || + start.line() > end.line() || + start.lineOffset() < 0 || end.lineOffset() < 0 || + startOffsetAfterEndOffset) { + raiseError(kind(tree) + " invalid range ", tree.metaData().textRange().toString(), start); + } + } + + private void assertTreeHasAtLeastOneToken(Tree tree) { + if (!(tree instanceof TopLevelTree) && tree.metaData().tokens().isEmpty()) { + raiseError(kind(tree) + " has no token", "", tree.textRange().start()); + } + } + + private void assertTokensMatchSourceCode(Tree tree, String code) { + CodeFormToken codeFormToken = new CodeFormToken(tree.metaData()); + codeFormToken.assertEqualTo(code); + } + + private void assertTokensAndChildTokens(Tree tree) { + assertTokensAreInsideRange(tree); + Set parentTokens = new HashSet<>(tree.metaData().tokens()); + Map childByToken = new HashMap<>(); + for (Tree child : tree.children()) { + if (child != null && child.metaData() != null && !isAllowedMisplacedTree(child)) { + assertChildRangeIsInsideParentRange(tree, child); + assertChildTokens(parentTokens, childByToken, tree, child); + } + } + parentTokens.removeAll(childByToken.keySet()); + assertUnexpectedTokenKind(tree, parentTokens); + } + + private static boolean isAllowedMisplacedTree(Tree tree) { + List tokens = tree.metaData().tokens(); + return tokens.size() == 1 && ALLOWED_MISPLACED_TOKENS_OUTSIDE_PARENT_RANGE.contains(tokens.get(0).text()); + } + + private void assertUnexpectedTokenKind(Tree tree, Set tokens) { + if (tree instanceof NativeTreeImpl || tree instanceof LiteralTreeImpl || tree instanceof PlaceHolderTreeImpl) { + return; + } + List unexpectedTokens; + if (tree instanceof IdentifierTree) { + unexpectedTokens = tokens.stream() + .filter(token -> token.type() == Token.Type.KEYWORD || token.type() == Token.Type.STRING_LITERAL) + .collect(Collectors.toList()); + } else { + unexpectedTokens = tokens.stream() + .filter(token -> token.type() != Token.Type.KEYWORD) + .filter(token -> !PUNCTUATOR_PATTERN.matcher(token.text()).matches()) + .filter(token -> !ALLOWED_MISPLACED_TOKENS_OUTSIDE_PARENT_RANGE.contains(token.text())) + .collect(Collectors.toList()); + } + if (!unexpectedTokens.isEmpty()) { + String tokenList = unexpectedTokens.stream() + .sorted(Comparator.comparing(token -> token.textRange().start())) + .map(Token::text) + .collect(Collectors.joining("', '")); + raiseError("Unexpected tokens in " + kind(tree), ": '" + tokenList + "'", tree.textRange().start()); + } + } + + private void assertTokensAreInsideRange(Tree tree) { + TextRange parentRange = tree.metaData().textRange(); + tree.metaData().tokens().stream() + .filter(token -> !ALLOWED_MISPLACED_TOKENS_OUTSIDE_PARENT_RANGE.contains(token.text())) + .filter(token -> !token.textRange().isInside(parentRange)) + .findFirst() + .ifPresent(token -> raiseError( + kind(tree) + " contains a token outside its range", + " range: " + parentRange + " tokenRange: " + token.textRange() + " token: '" + token.text() + "'", + token.textRange().start())); + } + + private void assertChildRangeIsInsideParentRange(Tree parent, Tree child) { + TextRange parentRange = parent.metaData().textRange(); + TextRange childRange = child.metaData().textRange(); + if (!childRange.isInside(parentRange)) { + raiseError(kind(parent) + " contains a child " + kind(child) + " outside its range", + ", parentRange: " + parentRange + " childRange: " + childRange, + childRange.start()); + } + } + + private void assertChildTokens(Set parentTokens, Map childByToken, Tree parent, Tree child) { + for (Token token : child.metaData().tokens()) { + if (!parentTokens.contains(token)) { + raiseError(kind(child) + " contains a token missing in its parent " + kind(parent), + ", token: '" + token.text() + "'", + token.textRange().start()); + } + Tree intersectingChild = childByToken.get(token); + if (intersectingChild != null) { + raiseError(kind(parent) + " has a token used by both children " + kind(intersectingChild) + " and " + kind(child), + ", token: '" + token.text() + "'", + token.textRange().start()); + } else { + childByToken.put(token, child); + } + } + } + + private class CodeFormToken { + + private final StringBuilder code = new StringBuilder(); + private final List commentsInside; + private int lastLine = 1; + private int lastLineOffset = 0; + private int lastComment = 0; + + private CodeFormToken(TreeMetaData metaData) { + this.commentsInside = metaData.commentsInside(); + metaData.tokens().forEach(this::add); + addRemainingComments(); + } + + private void add(Token token) { + while (lastComment < commentsInside.size() && + commentsInside.get(lastComment).textRange().start().compareTo(token.textRange().start()) < 0) { + Comment comment = commentsInside.get(lastComment); + addTextAt(comment.text(), comment.textRange()); + lastComment++; + } + addTextAt(token.text(), token.textRange()); + } + + private void addRemainingComments() { + for (int i = lastComment; i < commentsInside.size(); i++) { + addTextAt(commentsInside.get(i).text(), commentsInside.get(i).textRange()); + } + } + + private void addTextAt(String text, TextRange textRange) { + while (lastLine < textRange.start().line()) { + code.append("\n"); + lastLine++; + lastLineOffset = 0; + } + while (lastLineOffset < textRange.start().lineOffset()) { + code.append(' '); + lastLineOffset++; + } + code.append(text); + lastLine = textRange.end().line(); + lastLineOffset = textRange.end().lineOffset(); + } + + private void assertEqualTo(String expectedCode) { + String[] actualLines = lines(this.code.toString()); + String[] expectedLines = lines(expectedCode); + for (int i = 0; i < actualLines.length && i < expectedLines.length; i++) { + if (!actualLines[i].equals(expectedLines[i])) { + raiseError("Unexpected AST difference", ":\n" + + " Actual : " + actualLines[i] + "\n" + + " Expected : " + expectedLines[i] + "\n", + new TextPointerImpl(i + 1, 0)); + } + } + if (actualLines.length != expectedLines.length) { + raiseError( + "Unexpected AST number of lines", + " actual: " + actualLines.length + ", expected: " + expectedLines.length, + new TextPointerImpl(Math.min(actualLines.length, expectedLines.length), 0)); + } + } + + private String[] lines(String code) { + return code + .replace('\t', ' ') + .replaceFirst("[\r\n ]+$", "") + .split(" *(\r\n|\n|\r)", -1); + } + } + +} diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/converter/package-info.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/converter/package-info.java new file mode 100644 index 00000000..cf09ffee --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/converter/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.plugin.converter; diff --git a/slang-plugin/src/main/java/org/sonarsource/slang/plugin/package-info.java b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/package-info.java new file mode 100644 index 00000000..a3c6c59a --- /dev/null +++ b/slang-plugin/src/main/java/org/sonarsource/slang/plugin/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.plugin; diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/AbstractPropertyHandlerSensorTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/AbstractPropertyHandlerSensorTest.java new file mode 100644 index 00000000..186eb3ce --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/AbstractPropertyHandlerSensorTest.java @@ -0,0 +1,147 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; + +class AbstractPropertyHandlerSensorTest { + + private static final List ANALYSIS_WARNINGS = new ArrayList<>(); + private static final Path PROJECT_DIR = Paths.get("src", "test", "resources", "propertyHandler"); + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @BeforeEach + void setup() { + ANALYSIS_WARNINGS.clear(); + } + + @Test + void test_descriptor() throws Exception { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + PropertyHandlerSensorTester sensor = new PropertyHandlerSensorTester(); + sensor.describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Import of propertyName issues"); + assertThat(sensorDescriptor.languages()).containsOnly("dummy"); + assertThat(logTester.logs()).isEmpty(); + } + + @Test + void test_configuration() throws Exception { + PropertyHandlerSensorTester sensor = new PropertyHandlerSensorTester(); + assertThat(sensor.propertyKey()).isEqualTo("propertyKey"); + assertThat(sensor.propertyName()).isEqualTo("propertyName"); + assertThat(sensor.configurationKey()).isEqualTo("sonar.configuration.key"); + } + + @Test + void do_nothing_if_property_not_configured() throws Exception { + SensorContextTester context = createContext(PROJECT_DIR); + PropertyHandlerSensorTester sensor = new PropertyHandlerSensorTester(); + sensor.execute(context); + + assertThat(ANALYSIS_WARNINGS).isEmpty(); + assertThat(logTester.logs()).isEmpty(); + } + + @Test + void report_issue_if_file_missing() throws Exception { + SensorContextTester context = createContext(PROJECT_DIR); + PropertyHandlerSensorTester sensor = new PropertyHandlerSensorTester(); + context.settings().setProperty("sonar.configuration.key", "missing-file1.txt,missing-file2.txt,dummyReport.txt,missing-file3.txt"); + + sensor.execute(context); + List infos = logTester.logs(Level.INFO); + assertThat(infos).hasSize(1); + assertThat(infos.get(0)) + .startsWith("Importing") + .endsWith("dummyReport.txt"); + + List warnings = logTester.logs(Level.WARN); + assertThat(warnings) + .hasSize(1) + .hasSameSizeAs(ANALYSIS_WARNINGS); + assertThat(warnings.get(0)) + .startsWith("Unable to import propertyName report file(s):") + .contains("missing-file1.txt") + .contains("missing-file2.txt") + .contains("missing-file3.txt") + .doesNotContain("dummyReport.txt") + .endsWith("The report file(s) can not be found. Check that the property 'sonar.configuration.key' is correctly configured."); + assertThat(ANALYSIS_WARNINGS.get(0)) + .startsWith("Unable to import 3 propertyName report file(s).") + .endsWith("Please check that property 'sonar.configuration.key' is correctly configured and the analysis logs for more details."); + } + + private static SensorContextTester createContext(Path projectDir) throws IOException { + SensorContextTester context = SensorContextTester.create(projectDir); + Files.list(projectDir) + .filter(file -> !Files.isDirectory(file)) + .forEach(file -> addFileToContext(context, projectDir, file)); + return context; + } + + private static void addFileToContext(SensorContextTester context, Path projectDir, Path file) { + try { + String projectId = projectDir.getFileName().toString() + "-project"; + context.fileSystem().add(TestInputFileBuilder.create(projectId, projectDir.toFile(), file.toFile()) + .setCharset(UTF_8) + .setLanguage("dummy") + .setContents(new String(Files.readAllBytes(file), UTF_8)) + .setType(InputFile.Type.MAIN) + .build()); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } + + private static class PropertyHandlerSensorTester extends AbstractPropertyHandlerSensor { + + private PropertyHandlerSensorTester() { + super(ANALYSIS_WARNINGS::add, "propertyKey", "propertyName", "sonar.configuration.key", "dummy"); + } + + @Override + public Consumer reportConsumer(SensorContext context) { + return file -> { /* do nothing */ }; + } + } +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CommentAnalysisUtilsTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CommentAnalysisUtilsTest.java new file mode 100644 index 00000000..40ece399 --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CommentAnalysisUtilsTest.java @@ -0,0 +1,84 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.impl.CommentImpl; +import org.sonarsource.slang.impl.TextRangeImpl; + +import static org.assertj.core.api.Assertions.assertThat; + +class CommentAnalysisUtilsTest { + + private final static String CODE = "// NOSONAR \n" + + "def fun() { \n" + + "/* todo */ \n" + + "}"; + private static final TextRange CODE_TEXT_RANGE = new TextRangeImpl(1, 0, 4, 0); + + @Test + void testNosonarComment() { + TextRange noSonarCommentTextRange = new TextRangeImpl(1, 2, 1, 10); + Comment nosonarComment = new CommentImpl(CODE, " NOSONAR ", CODE_TEXT_RANGE, noSonarCommentTextRange); + assertThat(CommentAnalysisUtils.isNosonarComment(nosonarComment)).isTrue(); + } + + @Test + void testNotNosonarComment() { + TextRange todoCommentTextRange = new TextRangeImpl(3, 2, 3, 8); + Comment todoComment = new CommentImpl(CODE, " todo ", CODE_TEXT_RANGE, todoCommentTextRange); + assertThat(CommentAnalysisUtils.isNosonarComment(todoComment)).isFalse(); + } + + @Test + void testAddNonBlankSingleLineComment() { + testAddCommentLines("single line comment", + new TextRangeImpl(2, 2, 2, 17), + Set.of(2)); + } + + @Test + void testAddBlankSingleLineComment() { + testAddCommentLines("*#=|", + new TextRangeImpl(2, 2, 2, 6), + Set.of()); + } + + @Test + void testAddNonBlankMultiLineComment() { + testAddCommentLines("multi \nline \ncomment", + new TextRangeImpl(7, 2, 9, 7), + Set.of(7, 8, 9)); + } + + @Test + void testAddBlankMultiLineComment() { + testAddCommentLines(" \n#= \n*|", + new TextRangeImpl(7, 2, 9, 4), + Set.of()); + } + + private void testAddCommentLines(String comment, TextRange commentTextRange, Set expectedCommentLines) { + assertThat(CommentAnalysisUtils.findNonEmptyCommentLines(commentTextRange, comment)).containsAll(expectedCommentLines); + } +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CpdVisitorTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CpdVisitorTest.java new file mode 100644 index 00000000..6e66e49b --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CpdVisitorTest.java @@ -0,0 +1,68 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.File; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.cpd.internal.TokensLine; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.parser.SLangConverter; + +import static org.assertj.core.api.Assertions.assertThat; + +class CpdVisitorTest { + + @Test + void test(@TempDir File tempFolder) throws Exception { + File file = File.createTempFile("file", ".tmp", tempFolder); + String content = "import util; foo(x\n * 42 \n+ \"abc\");"; + SensorContextTester sensorContext = SensorContextTester.create(tempFolder); + DefaultInputFile inputFile = new TestInputFileBuilder("moduleKey", file.getName()) + .setContents(content) + .build(); + Tree root = new SLangConverter().parse(content); + InputFileContext ctx = new InputFileContext(sensorContext, inputFile); + new CpdVisitor().scan(ctx, root); + + List cpdTokenLines = sensorContext.cpdTokens(inputFile.key()); + assertThat(cpdTokenLines).hasSize(3); + + assertThat(cpdTokenLines.get(0).getValue()).isEqualTo("foo(x"); + assertThat(cpdTokenLines.get(0).getStartLine()).isEqualTo(1); + assertThat(cpdTokenLines.get(0).getStartUnit()).isEqualTo(1); + assertThat(cpdTokenLines.get(0).getEndUnit()).isEqualTo(3); + + assertThat(cpdTokenLines.get(1).getValue()).isEqualTo("*42"); + assertThat(cpdTokenLines.get(1).getStartLine()).isEqualTo(2); + assertThat(cpdTokenLines.get(1).getStartUnit()).isEqualTo(4); + assertThat(cpdTokenLines.get(1).getEndUnit()).isEqualTo(5); + + assertThat(cpdTokenLines.get(2).getValue()).isEqualTo("+LITERAL);"); + assertThat(cpdTokenLines.get(2).getStartLine()).isEqualTo(3); + assertThat(cpdTokenLines.get(2).getStartUnit()).isEqualTo(6); + assertThat(cpdTokenLines.get(2).getEndUnit()).isEqualTo(9); + } + +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CyclomaticComplexityVisitorTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CyclomaticComplexityVisitorTest.java new file mode 100644 index 00000000..0d38883b --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/CyclomaticComplexityVisitorTest.java @@ -0,0 +1,91 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.MatchCaseTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.parser.SLangConverter; +import java.util.List; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CyclomaticComplexityVisitorTest { + + private static List getComplexityTrees(String content) { + Tree root = new SLangConverter().parse(content); + return new CyclomaticComplexityVisitor().complexityTrees(root); + } + + @Test + void test_matchCases() throws Exception { + String content = "match (a) {" + + " 0 -> return \"none\";" + + " 1 -> return \"one\";" + + " 2 -> return \"many\";" + + " else -> return \"it's complicated\";" + + " };"; + List trees = getComplexityTrees(content); + assertThat(trees) + .hasSize(3) + .allMatch(tree -> tree instanceof MatchCaseTree); + } + + @Test + void test_functions_with_conditional() throws Exception { + String content = "void fun foo (a) {" + + " if (a == 2) {" + + " print(a + 1);" + + " } else {" + + " print(a);" + + " };" + + " }"; + List trees = getComplexityTrees(content); + assertThat(trees).hasSize(2); + assertThat(trees.get(0)).isInstanceOf(FunctionDeclarationTree.class); + assertThat(trees.get(1)).isInstanceOf(Token.class); + } + + @Test + void test_loops() throws Exception { + String content = + "for (var x = list) { " + + " while (x > y) { " + + " x = x-1;" + + " };" + + "};"; + List trees = getComplexityTrees(content); + assertThat(trees) + .hasSize(2) + .allMatch(tree -> tree instanceof LoopTree); + + content = "do { x = x-1; } while (x > y);"; + trees = getComplexityTrees(content); + assertThat(trees) + .hasSize(1) + .allMatch(tree -> tree instanceof LoopTree); + + } + +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/DurationStatisticsTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/DurationStatisticsTest.java new file mode 100644 index 00000000..59b73e26 --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/DurationStatisticsTest.java @@ -0,0 +1,79 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static org.assertj.core.api.Assertions.assertThat; + +class DurationStatisticsTest { + + private SensorContextTester sensorContext = SensorContextTester.create(Paths.get(".")); + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void statistics_disabled() { + DurationStatistics statistics = new DurationStatistics(sensorContext.config()); + fillStatistics(statistics); + statistics.log(); + assertThat(logTester.logs(Level.INFO)).isEmpty(); + } + + @Test + void statistics_activated() { + sensorContext.settings().setProperty("sonar.slang.duration.statistics", "true"); + DurationStatistics statistics = new DurationStatistics(sensorContext.config()); + fillStatistics(statistics); + statistics.log(); + assertThat(logTester.logs(Level.INFO)).hasSize(1); + assertThat(logTester.logs(Level.INFO).get(0)).startsWith("Duration Statistics, "); + } + + @Test + void statistics_format() { + sensorContext.settings().setProperty("sonar.slang.duration.statistics", "true"); + DurationStatistics statistics = new DurationStatistics(sensorContext.config()); + statistics.record("A", 12_000_000L); + statistics.record("B", 15_000_000_000L); + statistics.log(); + assertThat(logTester.logs(Level.INFO)).hasSize(1); + assertThat(logTester.logs(Level.INFO).get(0)).isEqualTo("Duration Statistics, B 15'000 ms, A 12 ms"); + } + + private void fillStatistics(DurationStatistics statistics) { + StringBuilder txt = new StringBuilder(); + statistics.time("A", () -> txt.append("1")).append(2); + statistics.time("B", () -> { + txt.append("3"); + }); + statistics + .time("C", (t, u) -> txt.append(t).append(u)) + .accept("4", "5"); + assertThat(txt).hasToString("12345"); + } + +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/MetricVisitorTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/MetricVisitorTest.java new file mode 100644 index 00000000..4be0ed69 --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/MetricVisitorTest.java @@ -0,0 +1,243 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonarsource.slang.parser.SLangConverter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class MetricVisitorTest { + + private File tempFolder; + private SLangConverter parser = new SLangConverter(); + private MetricVisitor visitor; + private SensorContextTester sensorContext; + private DefaultInputFile inputFile; + + @BeforeEach + void setUp(@TempDir File tempFolder) { + this.tempFolder = tempFolder; + sensorContext = SensorContextTester.create(tempFolder); + FileLinesContext mockFileLinesContext = mock(FileLinesContext.class); + FileLinesContextFactory mockFileLinesContextFactory = mock(FileLinesContextFactory.class); + when(mockFileLinesContextFactory.createFor(any(InputFile.class))).thenReturn(mockFileLinesContext); + visitor = new MetricVisitor(mockFileLinesContextFactory, SlangSensor.EXECUTABLE_LINE_PREDICATE); + } + + @Test + void emptySource() throws Exception { + scan(""); + assertThat(visitor.linesOfCode()).isEmpty(); + assertThat(visitor.commentLines()).isEmpty(); + assertThat(visitor.numberOfFunctions()).isZero(); + } + + @Test + void linesOfCode() throws Exception { + scan("" + + "x + 1;\n" + + "// comment\n" + + "fun function1() { // comment\n" + + "x = true || false; }"); + assertThat(visitor.linesOfCode()).containsExactly(1, 3, 4); + } + + @Test + void commentLines() throws Exception { + scan("" + + "x + 1;\n" + + "// comment\n" + + "fun function1() { // comment\n" + + "x = true || false; }"); + assertThat(visitor.commentLines()).containsExactly(2, 3); + } + + @Test + void commentBeforeTheFirstTokenCorrespondToTheIgnoredHeader() throws Exception { + scan("" + + "// first line of the header\n" + + "// second line of the header\n" + + "/*\n" + + " this is also part of the header\n" + + "*/\n" + + "package abc; // comment 1\n" + + "import x;\n" + + "\n" + + "fun function1() { // comment 2\n" + + " //\n" + + " /**/\n" + + "}\n"); + assertThat(visitor.commentLines()).containsExactly(6, 9); + } + + @Test + void commentsWithoutDeclarationsAreIgnored() throws Exception { + scan("" + + "// header 1\n" + + "/**\n" + + " * header 2\n" + + " */\n"); + assertThat(visitor.commentLines()).isEmpty(); + } + + @Test + void noSonarCommentsDoNotAccountForTheCommentMetrics() throws Exception { + scan("" + + "fun function1() {\n" + + " // comment1\n" + + " // NOSONAR comment2\n" + + " // comment3\n" + + "}\n"); + assertThat(visitor.commentLines()).containsExactly(2, 4); + } + + @Test + void emptyLinesDoNotAccountForTheCommentMetrics() throws Exception { + scan("" + + "package abc; // comment 1\n" + + "/*\n" + + "\n" + + " comment 2\n" + + " \n" + + " comment 3\n" + + "\n" + + "*/\n" + + "\n" + + "fun function1() { // comment 4\n" + + " /**\n" + + " *\n"+ + " #\n"+ + " =\n"+ + " -\n"+ + " |\n"+ + " | comment 5\n"+ + " | どのように\n"+ + " |\n"+ + " */\n"+ + "}\n"); + assertThat(visitor.commentLines()).containsExactlyInAnyOrder(1, 4, 6, 10, 17, 18); + } + + @Test + void multiLineComment() throws Exception { + scan("" + + "/*start\n" + + "x + 1\n" + + "end*/"); + assertThat(visitor.commentLines()).containsExactly(1, 2, 3); + assertThat(visitor.linesOfCode()).isEmpty(); + } + + @Test + void functions() throws Exception { + scan("" + + "x + 1;\n" + + "x = true || false;"); + assertThat(visitor.numberOfFunctions()).isZero(); + scan("" + + "x + 1;\n" + + "fun noBodyFunction();\n" + // Only functions with implementation bodies are considered for the metric + "fun() { x = 1; }\n" + // Anonymous functions are not considered for function metric computation + "fun function1() { // comment\n" + + "x = true || false; }"); + assertThat(visitor.numberOfFunctions()).isEqualTo(1); + } + + @Test + void classes() throws Exception { + scan("" + + "x + 1;\n" + + "x = true || false;"); + assertThat(visitor.numberOfClasses()).isZero(); + scan("" + + "class C {}\n" + + "fun function() {}\n" + + "class D { int val x = 0; }\n" + + "class E {\n" + + " fun doSomething(int x) {}\n" + + "}"); + assertThat(visitor.numberOfClasses()).isEqualTo(3); + } + + @Test + void cognitiveComplexity() throws Exception { + scan("" + + "class A { fun foo() { if(1 != 1) 1; } }" + // +1 for 'if' + "fun function() {" + + " if (1 != 1) {" + // +1 for 'if' + " if (1 != 1) {" + // + 2 for nested 'if' + " 1" + + " }" + + " };" + + " class B {" + // Nesting level reset here because of class declaration + " fun bar(int a) {" + + " match(a) {" + // +1 for match + " 1 -> doSomething();" + + " 2 -> doSomething();" + + " else -> if (1 != 1) doSomething();" + // +2 for nested 'if' + " }" + + " }" + + " };" + + "}"); + assertThat(visitor.cognitiveComplexity()).isEqualTo(7); + } + + @Test + void executable_lines() throws Exception { + scan("" + + "package abc;\n" + + "import x;\n" + + "class A {\n" + + " fun foo() {\n" + + " statementOnSeveralLines(a,\n" + + " b);\n" + + " };\n" + + "}\n" + + "{\n" + + " x = 42;\n" + + "};"); + assertThat(visitor.executableLines()).containsExactly(5, 10); + } + + private void scan(String code) throws IOException { + File tmpFile = File.createTempFile("file", ".tmp", tempFolder); + inputFile = new TestInputFileBuilder("moduleKey", tmpFile.getName()) + .setCharset(StandardCharsets.UTF_8) + .initMetadata(code).build(); + InputFileContext ctx = new InputFileContext(sensorContext, inputFile); + visitor.scan(ctx, parser.parse(code)); + } + +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/RulesDefinitionUtilsTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/RulesDefinitionUtilsTest.java new file mode 100644 index 00000000..42882259 --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/RulesDefinitionUtilsTest.java @@ -0,0 +1,111 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.Test; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.server.rule.RulesDefinitionAnnotationLoader; +import org.sonar.check.Rule; +import org.sonar.check.RuleProperty; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.checks.utils.PropertyDefaultValue; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class RulesDefinitionUtilsTest { + + private static final String REPOSITORY = "test"; + private RulesDefinition.NewRepository repository; + private RulesDefinition.Context context; + + @Test + void test_setDefaultValuesForParameters_ruby() { + initRepository(); + + RulesDefinitionUtils.setDefaultValuesForParameters(repository, Collections.singletonList(Check.class), Language.RUBY); + repository.done(); + + RulesDefinition.Repository repository = context.repository(REPOSITORY); + RulesDefinition.Rule check = repository.rule("check"); + RulesDefinition.Param param = check.param("param"); + assertThat(param.defaultValue()).isEqualTo("ruby"); + } + + @Test + void test_setDefaultValuesForParameters_scala() { + initRepository(); + + RulesDefinitionUtils.setDefaultValuesForParameters(repository, Collections.singletonList(Check.class), Language.SCALA); + repository.done(); + + RulesDefinition.Repository repository = context.repository(REPOSITORY); + RulesDefinition.Rule check = repository.rule("check"); + RulesDefinition.Param param = check.param("param"); + assertThat(param.defaultValue()).isEqualTo("scala"); + } + + @Test + void wrong_annotation() { + context = new RulesDefinition.Context(); + repository = context.createRepository(REPOSITORY, Language.SCALA.toString()); + new RulesDefinitionAnnotationLoader().load(repository, WrongAnnotationUsage.class); + + List> wrongAnnotationCheck = Collections.singletonList(WrongAnnotationUsage.class); + assertThatThrownBy( () -> RulesDefinitionUtils.setDefaultValuesForParameters(repository, wrongAnnotationCheck, Language.RUBY)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Invalid @PropertyDefaultValue on WrongAnnotationUsage for language RUBY"); + + assertThatThrownBy( () -> RulesDefinitionUtils.setDefaultValuesForParameters(repository, wrongAnnotationCheck, Language.SCALA)) + .isInstanceOf(IllegalStateException.class) + .hasMessage("Invalid @PropertyDefaultValue on WrongAnnotationUsage for language SCALA"); + } + + private void initRepository() { + context = new RulesDefinition.Context(); + repository = context.createRepository(REPOSITORY, Language.SCALA.toString()); + new RulesDefinitionAnnotationLoader().load(repository, Check.class); + } + + @Rule(key = "check", name = "Check", description = "Desc") + static class Check { + + @RuleProperty(key = "param") + @PropertyDefaultValue(language = Language.RUBY, defaultValue = "ruby") + @PropertyDefaultValue(language = Language.SCALA, defaultValue = "scala") + String param; + + String notAParamField; + + @RuleProperty(key = "paramNoDefault") + String paramNoDefault; + } + + @Rule(key = "invalid", name = "Check", description = "Desc") + static class WrongAnnotationUsage { + + @RuleProperty(key = "param") + @PropertyDefaultValue(language = Language.SCALA, defaultValue = "scala") + @PropertyDefaultValue(language = Language.SCALA, defaultValue = "ruby") + String param; + } +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SkipNoSonarLinesVisitorTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SkipNoSonarLinesVisitorTest.java new file mode 100644 index 00000000..be89c8c9 --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SkipNoSonarLinesVisitorTest.java @@ -0,0 +1,101 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.File; +import java.io.IOException; +import java.util.Set; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.issue.NoSonarFilter; +import org.sonarsource.slang.parser.SLangConverter; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +class SkipNoSonarLinesVisitorTest { + + private File tempFolder; + private SLangConverter sLangConverter; + + private NoSonarFilter mockNoSonarFilter; + private SkipNoSonarLinesVisitor visitor; + + @BeforeEach + void setUp(@TempDir File tempFolder) { + this.tempFolder = tempFolder; + this.sLangConverter = new SLangConverter(); + mockNoSonarFilter = mock(NoSonarFilter.class); + visitor = new SkipNoSonarLinesVisitor(mockNoSonarFilter); + } + + @Test + void testNoDeclarations() throws Exception { + testNosonarCommentLines("// NOSONAR comment\n", Set.of()); + } + + @Test + void testSingleNosonarComment() throws Exception { + testNosonarCommentLines("import something; \n" + + "// NOSONAR comment\n" + + "fun function1() { // comment\n" + + "x = true || false; }", + Set.of(2)); + } + + @Test + void testMultipleNosonarComments() throws IOException { + testNosonarCommentLines("/* File Header */" + + "import something; \n" + + "fun foo() { // NOSONAR\n" + + " // comment\n" + + "}\n" + + "\n" + + "fun bar() {\n" + + " // nosonar\n" + + " foo();\n" + + "}", + Set.of(2, 7)); + } + + private void testNosonarCommentLines(String content, Set expectedNosonarCommentLines) throws IOException { + InputFile inputFile = createInputFile(content); + + visitor.scan(createInputFileContext(inputFile), sLangConverter.parse(content)); + + verify(mockNoSonarFilter).noSonarInFile(inputFile, expectedNosonarCommentLines); + } + + private InputFile createInputFile(String content) throws IOException { + File file = File.createTempFile("file", ".tmp", tempFolder); + return new TestInputFileBuilder("moduleKey", file.getName()) + .setContents(content) + .build(); + } + + private InputFileContext createInputFileContext(InputFile inputFile){ + SensorContextTester sensorContext = SensorContextTester.create(tempFolder); + return new InputFileContext(sensorContext, inputFile); + } +} \ No newline at end of file diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SlangSensorTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SlangSensorTest.java new file mode 100644 index 00000000..b593c539 --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SlangSensorTest.java @@ -0,0 +1,501 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import org.junit.jupiter.api.Test; +import org.slf4j.event.Level; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.TextPointer; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.rule.Checks; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.batch.sensor.error.AnalysisError; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.batch.sensor.issue.IssueLocation; +import org.sonar.api.batch.sensor.issue.internal.DefaultNoSonarFilter; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.resources.Language; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Version; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.CommentedCodeCheck; +import org.sonarsource.slang.checks.IdenticalBinaryOperandCheck; +import org.sonarsource.slang.checks.StringLiteralDuplicatedCheck; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.parser.SLangConverter; +import org.sonarsource.slang.parser.SlangCodeVerifier; +import org.sonarsource.slang.testing.AbstractSensorTest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; +import static org.sonarsource.slang.plugin.SlangSensorTest.SlangLanguage.SLANG; +import static org.sonarsource.slang.testing.TextRangeAssert.assertTextRange; + +class SlangSensorTest extends AbstractSensorTest { + + @Test + void test_one_rule() { + InputFile inputFile = createInputFile("file1.slang", + "fun main() {\nprint (1 == 1);}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).hasSize(1); + Issue issue = issues.iterator().next(); + assertThat(issue.ruleKey().rule()).isEqualTo("S1764"); + IssueLocation location = issue.primaryLocation(); + assertThat(location.inputComponent()).isEqualTo(inputFile); + assertThat(location.message()).isEqualTo("Correct one of the identical sub-expressions on both sides this operator"); + assertTextRange(location.textRange()).hasRange(2, 12, 2, 13); + } + + @Test + void test_rule_with_gap() { + InputFile inputFile = createInputFile("file1.slang", + "fun f() { print(\"string literal\"); print(\"string literal\"); print(\"string literal\"); }"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1192"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).hasSize(1); + Issue issue = issues.iterator().next(); + assertThat(issue.ruleKey().rule()).isEqualTo("S1192"); + IssueLocation location = issue.primaryLocation(); + assertThat(location.inputComponent()).isEqualTo(inputFile); + assertThat(location.message()).isEqualTo("Define a constant instead of duplicating this literal \"string literal\" 3 times."); + assertTextRange(location.textRange()).hasRange(1, 16, 1, 32); + assertThat(issue.gap()).isEqualTo(2.0); + } + + @Test + void test_commented_code() { + InputFile inputFile = createInputFile("file1.slang", + "fun main() {\n" + + "// fun foo() { if (true) {print(\"string literal\");}}\n" + + "print (1 == 1);\n" + + "print(b);\n" + + "// a b c ...\n" + + "foo();\n" + + "// Coefficients of polynomial\n" + + "val b = DoubleArray(n); // linear\n" + + "val c = DoubleArray(n + 1); // quadratic\n" + + "val d = DoubleArray(n); // cubic\n" + + "}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S125"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).hasSize(1); + Issue issue = issues.iterator().next(); + assertThat(issue.ruleKey().rule()).isEqualTo("S125"); + IssueLocation location = issue.primaryLocation(); + assertThat(location.inputComponent()).isEqualTo(inputFile); + assertThat(location.message()).isEqualTo("Remove this commented out code."); + } + + @Test + void test_nosonar_commented_code() { + InputFile inputFile = createInputFile("file1.slang", + "fun main() {\n" + + "// fun foo() { if (true) {print(\"string literal\");}} NOSONAR\n" + + "print (1 == 1);\n" + + "print(b);\n" + + "// a b c ...\n" + + "foo();\n" + + "// Coefficients of polynomial\n" + + "val b = DoubleArray(n); // linear\n" + + "val c = DoubleArray(n + 1); // quadratic\n" + + "val d = DoubleArray(n); // cubic\n" + + "}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S125"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).isEmpty(); + } + + @Test + void simple_file() { + InputFile inputFile = createInputFile("file1.slang", + "fun main(int x) {\nprint (1 == 1); print(\"abc\"); }\nclass A {}"); + context.fileSystem().add(inputFile); + sensor(checkFactory()).execute(context); + assertThat(context.highlightingTypeAt(inputFile.key(), 1, 0)).containsExactly(TypeOfText.KEYWORD); + assertThat(context.highlightingTypeAt(inputFile.key(), 1, 3)).isEmpty(); + assertThat(context.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(3); + assertThat(context.measure(inputFile.key(), CoreMetrics.COMMENT_LINES).value()).isZero(); + assertThat(context.measure(inputFile.key(), CoreMetrics.FUNCTIONS).value()).isEqualTo(1); + assertThat(context.measure(inputFile.key(), CoreMetrics.CLASSES).value()).isEqualTo(1); + assertThat(context.cpdTokens(inputFile.key()).get(1).getValue()).isEqualTo("print(1==1);print(LITERAL);}"); + assertThat(context.measure(inputFile.key(), CoreMetrics.COMPLEXITY).value()).isEqualTo(1); + assertThat(context.measure(inputFile.key(), CoreMetrics.STATEMENTS).value()).isEqualTo(2); + + // FIXME + // assertThat(logTester.logs()).contains("1 source files to be analyzed"); + } + + @Test + void suppress_issues_in_class() { + InputFile inputFile = createInputFile("file1.slang", + "@Suppress(\"slang:S1764\")\n" + + "class { fun main() {\nprint (1 == 1);} }"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).isEmpty(); + } + + @Test + void suppress_issues_in_method() { + InputFile inputFile = createInputFile("file1.slang", + "class { " + + "@Suppress(\"slang:S1764\")\n" + + "fun suppressed() {\nprint (1 == 1);} " + + "fun notSuppressed() {\nprint (123 == 123);} " + + "}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).hasSize(1); + Issue issue = issues.iterator().next(); + IssueLocation location = issue.primaryLocation(); + assertTextRange(location.textRange()).hasRange(4, 14, 4, 17); + } + + @Test + void suppress_issues_in_var() { + InputFile inputFile = createInputFile("file1.slang", + "class A {void fun bar() {\n" + + "@Suppress(\"slang:S1764\")\n" + + "int val b = (1 == 1);\n" + + "int val c = (1 == 1);\n" + + "}}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).hasSize(1); + Issue issue = issues.iterator().next(); + IssueLocation location = issue.primaryLocation(); + assertTextRange(location.textRange()).hasRange(4, 18, 4, 19); + } + + @Test + void suppress_issues_in_parameter() { + InputFile inputFile = createInputFile("file1.slang", + "class A {void fun bar(@Suppress(\"slang:S1764\") int a = (1 == 1), int b = (1 == 1)) {} }"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).hasSize(1); + Issue issue = issues.iterator().next(); + IssueLocation location = issue.primaryLocation(); + assertTextRange(location.textRange()).hasRange(1, 79, 1, 80); + } + + @Test + void suppress_multiples_issues() { + InputFile inputFile = createInputFile("file1.slang", + "@Suppress(\"slang:S1764\", value=\"slang:S1192\")\n" + + "fun suppressed() {\nprint (1 == 1);print(\"string literal\"); print(\"string literal\"); print(\"string literal\"); } " + + "@Suppress(value={\"slang:S1764\",\"slang:S1192\"})\n" + + "fun suppressed() {\nprint (1 == 1);print(\"string literal\"); print(\"string literal\"); print(\"string literal\"); } " + + "@Suppress(\"slang:S1192\")\n" + + "@Suppress(\"slang:S1764\")\n" + + "fun suppressed() {\nprint (1 == 1);print(\"string literal\"); print(\"string literal\"); print(\"string literal\"); } " + + "@Suppress(value={\"slang:S1764\"})\n" + + "fun suppressed() {\nprint (1 == 1);}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764", "S1192"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).isEmpty(); + } + + @Test + void do_not_suppress_bad_key() { + InputFile inputFile = createInputFile("file1.slang", + "@Suppress(\"slang:S1234\")\n" + + "fun notSuppressed() {\nprint (1 == 1);} " + + "@Suppress(\"EQUALITY\")\n" + + "fun notSuppressed1() {\nprint (123 == 123);} " + + "@SuppressSonarIssue(\"slang:S1764\")\n" + + "fun notSuppressed2() {\nprint (1 == 1);}\n" + + "@Suppress(\"UNUSED_PARAMETER\")\n" + + "fun notSuppressed3() {\nprint (1 == 1);}\n" + + "@Suppress(\"unused_parameter\")\n" + + "fun notSuppressed4() {\nprint (1 == 1);} "); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).hasSize(5); + } + + @Test + void test_fail_input() throws IOException { + InputFile inputFile = createInputFile("fakeFile.slang", ""); + InputFile spyInputFile = spy(inputFile); + when(spyInputFile.contents()).thenThrow(IOException.class); + context.fileSystem().add(spyInputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection analysisErrors = context.allAnalysisErrors(); + assertThat(analysisErrors).hasSize(1); + AnalysisError analysisError = analysisErrors.iterator().next(); + assertThat(analysisError.inputFile()).isEqualTo(spyInputFile); + assertThat(analysisError.message()).isEqualTo("Unable to parse file: fakeFile.slang"); + assertThat(analysisError.location()).isNull(); + + assertThat(logTester.logs()).contains(String.format("Unable to parse file: %s. ", inputFile.uri())); + } + + @Test + void test_fail_parsing() { + InputFile inputFile = createInputFile("file1.slang", + "\n class A {\n" + + " fun x() {}\n" + + " fun y() {}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("ParsingError"); + sensor(checkFactory).execute(context); + + Collection issues = context.allIssues(); + assertThat(issues).hasSize(1); + Issue issue = issues.iterator().next(); + assertThat(issue.ruleKey().rule()).isEqualTo("ParsingError"); + IssueLocation location = issue.primaryLocation(); + assertThat(location.inputComponent()).isEqualTo(inputFile); + assertThat(location.message()).isEqualTo("A parsing error occurred in this file."); + assertTextRange(location.textRange()).hasRange(2, 0, 2, 10); + + Collection analysisErrors = context.allAnalysisErrors(); + assertThat(analysisErrors).hasSize(1); + AnalysisError analysisError = analysisErrors.iterator().next(); + assertThat(analysisError.inputFile()).isEqualTo(inputFile); + assertThat(analysisError.message()).isEqualTo("Unable to parse file: file1.slang"); + TextPointer textPointer = analysisError.location(); + assertThat(textPointer).isNotNull(); + assertThat(textPointer.line()).isEqualTo(2); + assertThat(textPointer.lineOffset()).isEqualTo(1); + + assertThat(logTester.logs()).contains(String.format("Unable to parse file: %s. Parse error at position 2:1", inputFile.uri())); + } + + @Test + void test_fail_parsing_without_parsing_error_rule_activated() { + InputFile inputFile = createInputFile("file1.slang", "{"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + assertThat(context.allIssues()).isEmpty(); + assertThat(context.allAnalysisErrors()).hasSize(1); + } + + @Test + void test_empty_file() { + InputFile inputFile = createInputFile("empty.slang", "\t\t \r\n \n "); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + sensor(checkFactory).execute(context); + Collection analysisErrors = context.allAnalysisErrors(); + assertThat(analysisErrors).isEmpty(); + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + assertThat(logTester.logs(Level.WARN)).isEmpty(); + } + + @Test + void test_failure_in_check() { + InputFile inputFile = createInputFile("file1.slang", "fun f() {}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = mock(CheckFactory.class); + var checks = mock(Checks.class); + SlangCheck failingCheck = init -> init.register(TopLevelTree.class, (ctx, tree) -> { + throw new IllegalStateException("BOUM"); + }); + when(checks.ruleKey(failingCheck)).thenReturn(RuleKey.of(repositoryKey(), "failing")); + when(checkFactory.create(repositoryKey())).thenReturn(checks); + when(checks.all()).thenReturn(Collections.singletonList(failingCheck)); + sensor(checkFactory).execute(context); + + Collection analysisErrors = context.allAnalysisErrors(); + assertThat(analysisErrors).hasSize(1); + AnalysisError analysisError = analysisErrors.iterator().next(); + assertThat(analysisError.inputFile()).isEqualTo(inputFile); + assertThat(logTester.logs()).contains("Cannot analyse 'file1.slang': BOUM"); + } + + @Test + void test_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + SlangSensor sensor = sensor(mock(CheckFactory.class)); + sensor.describe(sensorDescriptor); + assertThat(sensorDescriptor.languages()).hasSize(1); + assertThat(sensorDescriptor.languages()).containsExactly("slang"); + assertThat(sensorDescriptor.name()).isEqualTo("SLang Sensor"); + } + + @Test + void test_sonarlint_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + SlangSensor sensor = sensor(SonarRuntimeImpl.forSonarLint(Version.create(6, 5)), mock(CheckFactory.class)); + sensor.describe(sensorDescriptor); + assertThat(sensorDescriptor.languages()).hasSize(1); + assertThat(sensorDescriptor.languages()).containsExactly("slang"); + assertThat(sensorDescriptor.name()).isEqualTo("SLang Sensor"); + } + + @Test + void test_cancellation() { + InputFile inputFile = createInputFile("file1.slang", + "fun main() {\nprint (1 == 1);}"); + context.fileSystem().add(inputFile); + CheckFactory checkFactory = checkFactory("S1764"); + context.setCancelled(true); + sensor(checkFactory).execute(context); + Collection issues = context.allIssues(); + assertThat(issues).isEmpty(); + } + + @Test + void test_sonarlint_context() { + SonarRuntime sonarLintRuntime = SonarRuntimeImpl.forSonarLint(Version.create(3, 9)); + SensorContextTester context = SensorContextTester.create(baseDir); + InputFile inputFile = createInputFile("file1.slang", + "fun main(int x) {\nprint (1 == 1); print(\"abc\"); }\nclass A {}"); + context.fileSystem().add(inputFile); + context.setRuntime(sonarLintRuntime); + sensor(checkFactory("S1764")).execute(context); + + assertThat(context.allIssues()).hasSize(1); + + // No CPD, highlighting and metrics in SonarLint + assertThat(context.highlightingTypeAt(inputFile.key(), 1, 0)).isEmpty(); + assertThat(context.measure(inputFile.key(), CoreMetrics.NCLOC)).isNull(); + assertThat(context.cpdTokens(inputFile.key())).isNull(); + + // FIXME + // assertThat(logTester.logs()).contains("1 source files to be analyzed"); + } + + @Test + void test_sensor_descriptor_processes_files_independently() { + final SlangSensor sensor = sensor( + SonarRuntimeImpl.forSonarQube(Version.create(9, 3), SonarQubeSide.SCANNER, SonarEdition.DEVELOPER), + checkFactory()); + final boolean[] called = {false}; + SensorDescriptor descriptor = new DefaultSensorDescriptor() { + public SensorDescriptor processesFilesIndependently() { + called[0] = true; + return this; + } + }; + sensor.describe(descriptor); + + assertTrue(called[0]); + } + + @Test + void test_sensor_descriptor_processes_files_independently_no_reflection_failure() { + final SlangSensor sensor = sensor( + SonarRuntimeImpl.forSonarQube(Version.create(9, 3), SonarQubeSide.SCANNER, SonarEdition.DEVELOPER), + checkFactory()); + sensor.describe(new DefaultSensorDescriptor()); + assertThat(logTester.logs()) + .doesNotContain("Could not call SensorDescriptor.processesFilesIndependently() method"); + } + + @Override + protected String repositoryKey() { + return "slang"; + } + + @Override + protected Language language() { + return SLANG; + } + + private SlangSensor sensor(CheckFactory checkFactory) { + return sensor(SQ_LTS_RUNTIME, checkFactory); + } + + private SlangSensor sensor(SonarRuntime sonarRuntime, CheckFactory checkFactory) { + return new SlangSensor(sonarRuntime, new DefaultNoSonarFilter(), fileLinesContextFactory, SLANG) { + @Override + protected ASTConverter astConverter(SensorContext sensorContext) { + return new SLangConverter(); + } + + @Override + protected Checks checks() { + Checks checks = checkFactory.create(repositoryKey()); + checks.addAnnotatedChecks( + StringLiteralDuplicatedCheck.class, + new CommentedCodeCheck(new SlangCodeVerifier()), + IdenticalBinaryOperandCheck.class); + return checks; + } + + @Override + protected String repositoryKey() { + return SlangSensorTest.this.repositoryKey(); + } + }; + } + + enum SlangLanguage implements Language { + SLANG; + + @Override + public String getKey() { + return "slang"; + } + + @Override + public String getName() { + return "SLang"; + } + + @Override + public String[] getFileSuffixes() { + return new String[] {".slang"}; + } + } + +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/StatementsVisitorTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/StatementsVisitorTest.java new file mode 100644 index 00000000..a9650874 --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/StatementsVisitorTest.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.parser.SLangConverter; + +import static org.assertj.core.api.Assertions.assertThat; + +class StatementsVisitorTest { + + private static int statements(String content) { + Tree root = new SLangConverter().parse(content); + return new StatementsVisitor().statements(root); + } + + @Test + void should_count_top_level_without_natives_and_blocks() throws Exception { + String content = "" + + "package abc;" + + "import xyz;" + + "native[] { };" + + "foo;" + // +1 + "class A{};" + + "if (a) {};" + // +1 + "fun foo() {};" + + "{};"; + assertThat(statements(content)).isEqualTo(2); + } + + @Test + void should_count_statements_inside_blocks() throws Exception { + String content = "fun foo() {" + + "native[] { };" + // +1 + "foo;" + // +1 + "if (a) { foo; bar; };" + // +1 +1 +1 + "class A{};" + + "fun bar(){};" + + "};" + + "{ 2; 3; };"; // +1 +1 + assertThat(statements(content)).isEqualTo(7); + } + +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SyntaxHighlighterTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SyntaxHighlighterTest.java new file mode 100644 index 00000000..dc2c770b --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/SyntaxHighlighterTest.java @@ -0,0 +1,139 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; +import javax.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonarsource.slang.parser.SLangConverter; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.COMMENT; +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.CONSTANT; +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.KEYWORD; +import static org.sonar.api.batch.sensor.highlighting.TypeOfText.STRING; + +class SyntaxHighlighterTest { + + private SyntaxHighlighter highlightingVisitor = new SyntaxHighlighter(); + + private SensorContextTester sensorContext; + + private SLangConverter parser = new SLangConverter(); + + private DefaultInputFile inputFile; + + private File tempFolder; + + @BeforeEach + void setUp(@TempDir File tempFolder) { + this.tempFolder = tempFolder; + sensorContext = SensorContextTester.create(tempFolder); + } + + private void highlight(String code) throws IOException { + File tmpFile = File.createTempFile("file", ".tmp", tempFolder); + inputFile = new TestInputFileBuilder("moduleKey", tmpFile.getName()) + .setCharset(StandardCharsets.UTF_8) + .initMetadata(code).build(); + InputFileContext ctx = new InputFileContext(sensorContext, inputFile); + highlightingVisitor.scan(ctx, parser.parse(code)); + } + + private void assertHighlighting(int columnFirst, int columnLast, @Nullable TypeOfText type) { + assertHighlighting(1, columnFirst, columnLast, type); + } + + private void assertHighlighting(int line, int columnFirst, int columnLast, @Nullable TypeOfText type) { + for (int i = columnFirst; i <= columnLast; i++) { + List typeOfTexts = sensorContext.highlightingTypeAt(inputFile.key(), line, i); + if (type != null) { + assertThat(typeOfTexts).as("Expect highlighting " + type + " at line " + line + " lineOffset " + i).containsExactly(type); + } else { + assertThat(typeOfTexts).as("Expect no highlighting at line " + line + " lineOffset " + i).containsExactly(); + } + } + } + + @Test + void empty_input() throws Exception { + highlight(""); + assertHighlighting(1, 0, 0, null); + } + + @Test + void single_line_comment() throws Exception { + highlight(" // Comment "); + assertHighlighting(0, 1, null); + assertHighlighting(2, 12, COMMENT); + } + + @Test + void comment() throws Exception { + highlight(" /*Comment*/ "); + assertHighlighting(0, 1, null); + assertHighlighting(2, 12, COMMENT); + assertHighlighting(13, 13, null); + } + + @Test + void multiline_comment() throws Exception { + highlight("/*\nComment\n*/ "); + assertHighlighting(1, 0, 1, COMMENT); + assertHighlighting(2, 0, 6, COMMENT); + assertHighlighting(3, 0, 1, COMMENT); + assertHighlighting(3, 2, 2, null); + } + + @Test + void keyword() throws Exception { + highlight("fun foo() { if(x) y; }"); + assertHighlighting(0, 2, KEYWORD); + assertHighlighting(3, 11, null); + assertHighlighting(12, 13, KEYWORD); + assertHighlighting(14, 22, null); + } + + @Test + void string_literal() throws Exception { + highlight("x + \"abc\" + y;"); + assertHighlighting(1, 3, null); + assertHighlighting(4, 8, STRING); + assertHighlighting(9, 9, null); + } + + @Test + void numeric_literal() throws Exception { + highlight("x + 123 + y;"); + assertHighlighting(1, 3, null); + assertHighlighting(4, 6, CONSTANT); + assertHighlighting(7, 7, null); + } + +} diff --git a/slang-plugin/src/test/java/org/sonarsource/slang/plugin/converter/ASTConverterValidationTest.java b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/converter/ASTConverterValidationTest.java new file mode 100644 index 00000000..2944597a --- /dev/null +++ b/slang-plugin/src/test/java/org/sonarsource/slang/plugin/converter/ASTConverterValidationTest.java @@ -0,0 +1,468 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.plugin.converter; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.assertj.core.api.ListAssert; +import org.junit.jupiter.api.Test; +import org.sonar.api.config.Configuration; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.NativeKind; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.BlockTreeImpl; +import org.sonarsource.slang.impl.CommentImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.StringLiteralTreeImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.TokenImpl; +import org.sonarsource.slang.impl.TopLevelTreeImpl; +import org.sonarsource.slang.plugin.converter.ASTConverterValidation.ValidationMode; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +class ASTConverterValidationTest { + + private static final NativeKind NATIVE_KIND = new NativeKind() { + }; + + @Test + void delegate_calls() { + Tree tree = identifier(1, 0, "code"); + SimpleConverter wrappedConverter = new SimpleConverter(tree); + ASTConverterValidation validationConverter = new ASTConverterValidation(wrappedConverter, ValidationMode.LOG_ERROR); + assertThat(validationConverter.parse("code")).isSameAs(tree); + assertThat(wrappedConverter.isTerminated).isFalse(); + validationConverter.terminate(); + assertThat(wrappedConverter.isTerminated).isTrue(); + assertThat(validationConverter.errors()).isEmpty(); + + assertThat(validationConverter.parse("BOOM")).isSameAs(tree); + validationConverter.terminate(); + assertThat(wrappedConverter.isTerminated).isTrue(); + assertThat(validationConverter.errors()).containsExactly("Unexpected AST difference:\n" + + " Actual : code\n" + + " Expected : BOOM\n" + + " (line: 1, column: 1)"); + } + + @Test + void wrap() { + SimpleConverter wrappedConverter = new SimpleConverter(null); + String configKey = "sonar.slang.converter.validation"; + assertThat(ASTConverterValidation.wrap(wrappedConverter, new SimpleConfig(configKey, null))).isSameAs(wrappedConverter); + + ASTConverter converter = ASTConverterValidation.wrap(wrappedConverter, new SimpleConfig(configKey, "throw")); + assertThat(converter).isInstanceOf(ASTConverterValidation.class); + assertThat(((ASTConverterValidation) converter).mode()).isEqualTo(ValidationMode.THROW_EXCEPTION); + + converter = ASTConverterValidation.wrap(wrappedConverter, new SimpleConfig(configKey, "log")); + assertThat(converter).isInstanceOf(ASTConverterValidation.class); + assertThat(((ASTConverterValidation) converter).mode()).isEqualTo(ValidationMode.LOG_ERROR); + } + + @Test + void wrap_error() { + SimpleConverter wrappedConverter = new SimpleConverter(null); + String configKey = "sonar.slang.converter.validation"; + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> ASTConverterValidation.wrap(wrappedConverter, new SimpleConfig(configKey, "BOOM"))); + assertThat(e).hasMessage("Unsupported mode: BOOM"); + } + + @Test + void text_range() { + assertValidationErrors(" a", identifier(1, 4, 2, 0, "a")) + .isEmpty(); + + assertValidationErrors("a", identifier(0, 0, 1, 0, "a")) + .containsExactly("IdentifierTreeImpl invalid range TextRange[0, 0, 1, 0] (line: 0, column: 1)"); + + assertValidationErrors("a", identifier(1, -1, 1, 0, "a")) + .containsExactly("IdentifierTreeImpl invalid range TextRange[1, -1, 1, 0] (line: 1, column: 0)"); + + assertValidationErrors(" a", identifier(1, 4, 2, -1, "a")) + .containsExactly("IdentifierTreeImpl invalid range TextRange[1, 4, 2, -1] (line: 1, column: 5)"); + + assertValidationErrors(" a", identifier(1, 4, 1, 4, "a")) + .containsExactly( + "IdentifierTreeImpl contains a token outside its range range: TextRange[1, 4, 1, 4] tokenRange: TextRange[1, 4, 1, 5] token: 'a' (line: 1, column: 5)", + "IdentifierTreeImpl invalid range TextRange[1, 4, 1, 4] (line: 1, column: 5)"); + + assertValidationErrors("a", identifier(1, 0, 0, 0, "a")) + .containsExactly( + "IdentifierTreeImpl contains a token outside its range range: TextRange[1, 0, 0, 0] tokenRange: TextRange[1, 0, 1, 1] token: 'a' (line: 1, column: 1)", + "IdentifierTreeImpl invalid range TextRange[1, 0, 0, 0] (line: 1, column: 1)"); + + assertValidationErrors("\n\na", identifier(3, 0, 2, 0, "a")) + .containsExactly( + "IdentifierTreeImpl contains a token outside its range range: TextRange[3, 0, 2, 0] tokenRange: TextRange[3, 0, 3, 1] token: 'a' (line: 3, column: 1)", + "IdentifierTreeImpl invalid range TextRange[3, 0, 2, 0] (line: 3, column: 1)"); + } + + @Test + void missing_token() { + TextRange range = new TextRangeImpl(1, 0, 1, 1); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> assertValidationErrors("", new IdentifierTreeImpl(metaData(range), "a"), ValidationMode.THROW_EXCEPTION)); + assertThat(e).hasMessageContaining("IdentifierTreeImpl has no token"); + } + + @Test + void top_level_tree_can_have_zero_token() { + TopLevelTree topLevelTree = new TopLevelTreeImpl( + metaData(new TextRangeImpl(1, 0, 1, 0)), + Collections.emptyList(), + Collections.emptyList()); + assertValidationErrors("", topLevelTree).isEmpty(); + } + + @Test + void tokens_and_comments_match_source_code() { + Token token1 = keyword(1, 0, "package"); + Token token2 = token(2, 2, "abc"); + Comment comment1 = comment(1, 8, "/* comment1 */"); + Comment comment2 = comment(2, 6, "/* comment2 */"); + Tree tree = new NativeTreeImpl( + metaData(Arrays.asList(token1, token2), Arrays.asList(comment1, comment2)), + NATIVE_KIND, + Collections.emptyList()); + String code = "package /* comment1 */ \n abc /* comment2 */"; + assertValidationErrors(code, tree).isEmpty(); + } + + @Test + void missing_token_compare_to_source_code() { + Token token = keyword(1, 0, "package"); + Tree tree = new NativeTreeImpl( + metaData(Collections.singletonList(token), Collections.emptyList()), + NATIVE_KIND, + Collections.emptyList()); + String code = "package /* comment */"; + assertValidationErrors(code, tree) + .containsExactly("Unexpected AST difference:\n" + + " Actual : package\n" + + " Expected : package /* comment */\n" + + " (line: 1, column: 1)"); + } + + @Test + void source_code_has_unexpected_lines() { + Token token1 = keyword(1, 0, "package"); + Tree tree = new NativeTreeImpl(metaData(Collections.singletonList(token1), Collections.emptyList()), NATIVE_KIND, Collections.emptyList()); + String code = "package\n/* comment */"; + assertValidationErrors(code, tree) + .containsExactly("Unexpected AST number of lines actual: 1, expected: 2 (line: 1, column: 1)"); + } + + @Test + void extra_token_not_in_source_code() { + Token token1 = keyword(1, 0, "package"); + Token token2 = token(2, 2, "abc"); + Tree tree = new NativeTreeImpl( + metaData(Arrays.asList(token1, token2), Collections.emptyList()), + NATIVE_KIND, + Collections.emptyList()); + String code = "package"; + assertValidationErrors(code, tree) + .containsExactly("Unexpected AST number of lines actual: 2, expected: 1 (line: 1, column: 1)"); + } + + @Test + void native_tree_and_literal_accept_any_tokens() { + TreeMetaData metaData = metaData( + keyword(1, 0, "if"), + token(1, 3, "value"), + token(1, 9, "=="), + stringLiteral(1, 12, "\"a\"")); + + assertValidationErrors("if value == \"a\"", new NativeTreeImpl(metaData, NATIVE_KIND, Collections.emptyList())) + .isEmpty(); + + assertValidationErrors("if value == \"a\"", new StringLiteralTreeImpl(metaData, "\"a\"")) + .isEmpty(); + } + + @Test + void identifier_tree_does_not_accept_keyword_and_string_literal_tokens() { + TreeMetaData metaData = metaData( + keyword(1, 0, "if"), + token(1, 3, "value"), + token(1, 9, "=="), + stringLiteral(1, 12, "\"a\"")); + assertValidationErrors("if value == \"a\"", new IdentifierTreeImpl(metaData, "value")) + .containsExactly("Unexpected tokens in IdentifierTreeImpl: 'if', '\"a\"' (line: 1, column: 1)"); + } + + @Test + void non_identifier_tree_does_not_accept_identifier_tokens() { + TreeMetaData metaData = metaData( + keyword(1, 0, "if"), + token(1, 3, "value"), + token(1, 9, "=="), + stringLiteral(1, 12, "\"a\"")); + assertValidationErrors("if value == \"a\"", new BlockTreeImpl(metaData, Collections.emptyList())) + .containsExactly("Unexpected tokens in BlockTreeImpl: 'value', '\"a\"' (line: 1, column: 1)"); + } + + @Test + void child_range_or_token_inside_parent_range() { + Token identifierToken = token(1, 0, "value"); + IdentifierTreeImpl identifier = new IdentifierTreeImpl(metaData(identifierToken), "value"); + BlockTreeImpl block = new BlockTreeImpl(metaData(identifierToken), Collections.singletonList(identifier)); + + assertValidationErrors("value", block).isEmpty(); + } + + @Test + void allowed_misplaced_token() { + Token misplacedToken = token(1, 0, "implicit"); + Tree misplacedTree = new NativeTreeImpl(metaData(misplacedToken), NATIVE_KIND, Collections.emptyList()); + + Token identifierToken = token(1, 9, "value"); + IdentifierTreeImpl identifierTree = new IdentifierTreeImpl(metaData(identifierToken), "value"); + + TreeMetaData blockMetaData = metaData(identifierTree.textRange(), misplacedToken, identifierToken); + BlockTreeImpl block = new BlockTreeImpl(blockMetaData, Arrays.asList(misplacedTree, identifierTree)); + + assertValidationErrors("implicit value", block).isEmpty(); + } + + @Test + void child_range_or_token_outside_parent_range() { + Token ifToken = keyword(1, 0, "if"); + Token identifierToken = token(1, 3, "value"); + IdentifierTreeImpl identifier = new IdentifierTreeImpl(metaData(identifierToken), "value"); + BlockTreeImpl block = new BlockTreeImpl(metaData(ifToken), Collections.singletonList(identifier)); + + assertValidationErrors("if", block).containsExactly( + "BlockTreeImpl contains a child IdentifierTreeImpl outside its range, parentRange: TextRange[1, 0, 1, 2] childRange: TextRange[1, 3, 1, 8] (line: 1, column: 4)", + "IdentifierTreeImpl contains a token missing in its parent BlockTreeImpl, token: 'value' (line: 1, column: 4)"); + } + + @Test + void several_children_outside_parent_range() { + Token ifToken = keyword(1, 0, "if"); + Token annotationToken = token(1, 3, "@transient"); + Token identifierToken = token(1, 14, "value"); + IdentifierTreeImpl identifier = new IdentifierTreeImpl(metaData(annotationToken, identifierToken), "value"); + BlockTreeImpl block = new BlockTreeImpl(metaData(ifToken.textRange(), ifToken, annotationToken, identifierToken), Collections.singletonList(identifier)); + + assertValidationErrors("if @transient value", block).containsExactly( + "BlockTreeImpl contains a child IdentifierTreeImpl outside its range, parentRange: TextRange[1, 0, 1, 2] childRange: TextRange[1, 3, 1, 19] (line: 1, column: 4)", + "BlockTreeImpl contains a token outside its range range: TextRange[1, 0, 1, 2] tokenRange: TextRange[1, 3, 1, 13] token: '@transient' (line: 1, column: 4)"); + } + + @Test + void same_token_in_two_children() { + Token identifierToken = token(1, 0, "value"); + IdentifierTreeImpl identifier1 = new IdentifierTreeImpl(metaData(identifierToken), "value"); + IdentifierTreeImpl identifier2 = new IdentifierTreeImpl(metaData(identifierToken), "value"); + BlockTreeImpl block = new BlockTreeImpl(metaData(identifierToken), Arrays.asList(identifier1, identifier2)); + + assertValidationErrors("value", block) + .containsExactly("BlockTreeImpl has a token used by both children IdentifierTreeImpl and IdentifierTreeImpl, token: 'value' (line: 1, column: 1)"); + } + + @Test + void report_null_child_and_null_metaData() { + Token token = keyword(1, 0, "if"); + IdentifierTreeImpl identifier = new IdentifierTreeImpl(null, "if"); + BlockTreeImpl block = new BlockTreeImpl(metaData(token), Arrays.asList(identifier, null)); + + assertValidationErrors("if", block) + .containsExactly( + "BlockTreeImpl has a null child (line: 1, column: 1)", + "IdentifierTreeImpl metaData is null (line: 1, column: 1)"); + } + + @Test + void source_file_name_in_logs_when_set() { + String fileName = "my/file/name.java"; + String code = "a"; + Tree tree = identifier(0, 0, 1, 0, "a"); + SimpleConverter wrappedConverter = new SimpleConverter(tree); + ASTConverterValidation validationConverter = new ASTConverterValidation(wrappedConverter, ValidationMode.LOG_ERROR); + assertThat(validationConverter.parse(code, fileName)).isSameAs(tree); + assertThat(validationConverter.errors()) + .containsExactly("IdentifierTreeImpl invalid range TextRange[0, 0, 1, 0] (line: 0, column: 1) in file: " + fileName); + } + + private ListAssert assertValidationErrors(String code, Tree tree) { + return assertValidationErrors(code, tree, ValidationMode.LOG_ERROR); + } + + private ListAssert assertValidationErrors(String code, Tree tree, ValidationMode mode) { + SimpleConverter wrappedConverter = new SimpleConverter(tree); + ASTConverterValidation validationConverter = new ASTConverterValidation(wrappedConverter, mode); + assertThat(validationConverter.parse(code)).isSameAs(tree); + return assertThat(validationConverter.errors()); + } + + public static IdentifierTree identifier(int startLine, int startLineOffset, int endLine, int endLineOffset, String text) { + Token token = token(startLine, startLineOffset, text); + TextRange range = new TextRangeImpl(startLine, startLineOffset, endLine, endLineOffset); + return new IdentifierTreeImpl(metaData(range, token), text); + } + + public static IdentifierTree identifier(int line, int lineOffset, String text) { + Token token = token(line, lineOffset, text); + return new IdentifierTreeImpl(metaData(token), text); + } + + public static TreeMetaData metaData(Token... tokens) { + return metaData(Arrays.asList(tokens), Collections.emptyList()); + } + + public static TreeMetaData metaData(List tokens, List comments) { + List textRanges = Stream.of(tokens, comments) + .flatMap(List::stream) + .map(HasTextRange::textRange) + .collect(Collectors.toList()); + + TextPointer start = textRanges.stream().map(TextRange::start).min(Comparator.naturalOrder()).orElse(null); + TextPointer end = textRanges.stream().map(TextRange::end).max(Comparator.naturalOrder()).orElse(null); + TextRange textRange = new TextRangeImpl(start, end); + + return metaData(textRange, tokens, comments); + } + + public static TreeMetaData metaData(TextRange textRange, Token... tokens) { + return metaData(textRange, Arrays.asList(tokens), Collections.emptyList()); + } + + private static TreeMetaData metaData(TextRange textRange, List tokens, List comments) { + return new TreeMetaData() { + @Override + public TextRange textRange() { + return textRange; + } + + @Override + public List commentsInside() { + return comments; + } + + @Override + public List annotations() { + return Collections.emptyList(); + } + + @Override + public List tokens() { + return tokens; + } + + @Override + public Set linesOfCode() { + throw new UnsupportedOperationException(); + } + }; + } + + public static Comment comment(int line, int lineOffset, String text) { + String textContent = text.substring(2, text.length() - 2); + return new CommentImpl(text, textContent, range(line, lineOffset, text), range(line, lineOffset + 2, textContent)); + } + + public static Token keyword(int line, int lineOffset, String text) { + return new TokenImpl(range(line, lineOffset, text), text, Token.Type.KEYWORD); + } + + public static Token stringLiteral(int line, int lineOffset, String text) { + return new TokenImpl(range(line, lineOffset, text), text, Token.Type.STRING_LITERAL); + } + + public static Token token(int line, int lineOffset, String text) { + return new TokenImpl(range(line, lineOffset, text), text, Token.Type.OTHER); + } + + private static TextRange range(int line, int lineOffset, String text) { + String[] lines = text.split("\r\n|\n|\r", -1); + int endLineOffset = lines.length == 1 ? lineOffset + text.length() : lines[lines.length - 1].length(); + return new TextRangeImpl(line, lineOffset, line + lines.length - 1, endLineOffset); + } + + private static class SimpleConverter implements ASTConverter { + + private Tree parsedTreeToReturn; + + private boolean isTerminated = false; + + public SimpleConverter(Tree parsedTreeToReturn) { + this.parsedTreeToReturn = parsedTreeToReturn; + } + + @Override + public Tree parse(String content) { + return parsedTreeToReturn; + } + + @Override + public void terminate() { + isTerminated = true; + } + } + + private class SimpleConfig implements Configuration { + + private final String key; + private final String value; + + private SimpleConfig(String key, String value) { + this.key = key; + this.value = value; + } + + @Override + public Optional get(String key) { + return Optional.ofNullable(this.key.equals(key) ? value : null); + } + + @Override + public boolean hasKey(String key) { + return this.key.equals(key); + } + + @Override + public String[] getStringArray(String key) { + throw new UnsupportedOperationException(); + } + + } +} diff --git a/slang-plugin/src/test/resources/propertyHandler/dummyReport.txt b/slang-plugin/src/test/resources/propertyHandler/dummyReport.txt new file mode 100644 index 00000000..48cdce85 --- /dev/null +++ b/slang-plugin/src/test/resources/propertyHandler/dummyReport.txt @@ -0,0 +1 @@ +placeholder diff --git a/slang-plugin/src/test/resources/sensitive-data.txt b/slang-plugin/src/test/resources/sensitive-data.txt new file mode 100644 index 00000000..71517481 --- /dev/null +++ b/slang-plugin/src/test/resources/sensitive-data.txt @@ -0,0 +1 @@ +president-password=123456 diff --git a/slang-testing/build.gradle b/slang-testing/build.gradle new file mode 100644 index 00000000..ad2fe786 --- /dev/null +++ b/slang-testing/build.gradle @@ -0,0 +1,15 @@ +dependencies { + implementation project(':slang-api') + implementation 'com.google.code.findbugs:jsr305' + implementation 'org.sonarsource.analyzer-commons:sonar-analyzer-test-commons' + implementation 'org.sonarsource.api.plugin:sonar-plugin-api' + implementation 'org.sonarsource.sonarqube:sonar-plugin-api-impl' + implementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures' + implementation 'org.slf4j:slf4j-api' + implementation 'ch.qos.logback:logback-classic' + implementation "org.junit.jupiter:junit-jupiter-api" + testImplementation "org.junit.jupiter:junit-jupiter-engine" + implementation 'io.github.classgraph:classgraph' + implementation 'org.assertj:assertj-core' + implementation 'org.mockito:mockito-core' +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/AbstractSensorTest.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/AbstractSensorTest.java new file mode 100644 index 00000000..9871ba56 --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/AbstractSensorTest.java @@ -0,0 +1,92 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.rule.internal.NewActiveRule; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonar.api.resources.Language; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.Version; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public abstract class AbstractSensorTest { + + protected File baseDir; + protected SensorContextTester context; + protected FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class); + public static final SonarRuntime SQ_LTS_RUNTIME = SonarRuntimeImpl.forSonarQube(Version.create(8, 9), SonarQubeSide.SCANNER, SonarEdition.DEVELOPER); + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @BeforeEach + public void setup(@TempDir File tmpBaseDir) { + baseDir = tmpBaseDir; + context = SensorContextTester.create(baseDir); + FileLinesContext fileLinesContext = mock(FileLinesContext.class); + when(fileLinesContextFactory.createFor(any(InputFile.class))).thenReturn(fileLinesContext); + } + + protected CheckFactory checkFactory(String... ruleKeys) { + ActiveRulesBuilder builder = new ActiveRulesBuilder(); + for (String ruleKey : ruleKeys) { + NewActiveRule newRule = new NewActiveRule.Builder() + .setRuleKey(RuleKey.of(repositoryKey(), ruleKey)) + .setName(ruleKey) + .build(); + builder.addRule(newRule); + } + context.setActiveRules(builder.build()); + return new CheckFactory(context.activeRules()); + } + + protected InputFile createInputFile(String relativePath, String content) { + return new TestInputFileBuilder("moduleKey", relativePath) + .setModuleBaseDir(baseDir.toPath()) + .setType(InputFile.Type.MAIN) + .setLanguage(language().getKey()) + .setCharset(StandardCharsets.UTF_8) + .setContents(content) + .build(); + } + + protected abstract String repositoryKey(); + + protected abstract Language language(); + +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/PackageScanner.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/PackageScanner.java new file mode 100644 index 00000000..cc26c765 --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/PackageScanner.java @@ -0,0 +1,54 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ScanResult; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class PackageScanner { + + private PackageScanner() { + // static usage only + } + + /** + * Returns the fully qualified names (FQNs) of the classes inside @packageName implementing SlangCheck. + * @param packageName Used to filter classes - the FQN of a class contains the package name. + * @return A list of slang checks (FQNs). + */ + public static List findSlangChecksInPackage(String packageName) { + try (ScanResult scanResult = new ClassGraph().enableAllInfo().acceptPackages(packageName).scan()) { + Map allClasses = scanResult.getAllClassesAsMap(); + List testClassesInPackage = new ArrayList<>(); + for (Map.Entry classInfoEntry : allClasses.entrySet()) { + String name = classInfoEntry.getKey(); + ClassInfo classInfo = classInfoEntry.getValue(); + if (name.startsWith(packageName) && classInfo.getInterfaces().stream().anyMatch(i -> i.getSimpleName().equals("SlangCheck"))) { + testClassesInPackage.add(classInfo.getName()); + } + } + return testClassesInPackage; + } + } +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/RangeAssert.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/RangeAssert.java new file mode 100644 index 00000000..ff6adc52 --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/RangeAssert.java @@ -0,0 +1,44 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.impl.TextRanges; +import org.assertj.core.api.AbstractAssert; + +import static org.assertj.core.api.Assertions.assertThat; + +public class RangeAssert extends AbstractAssert { + + public RangeAssert(TextRange actual) { + super(actual, RangeAssert.class); + } + + public RangeAssert hasRange(int startLine, int startLineOffset, int endLine, int endLineOffset) { + isNotNull(); + assertThat(actual).isEqualTo(TextRanges.range(startLine, startLineOffset, endLine, endLineOffset)); + return this; + } + + public static RangeAssert assertRange(TextRange actual) { + return new RangeAssert(actual); + } + +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/TextRangeAssert.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/TextRangeAssert.java new file mode 100644 index 00000000..1de42348 --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/TextRangeAssert.java @@ -0,0 +1,47 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import javax.annotation.Nullable; +import org.assertj.core.api.AbstractAssert; +import org.sonar.api.batch.fs.TextRange; + +import static org.assertj.core.api.Assertions.assertThat; + +public class TextRangeAssert extends AbstractAssert { + + public TextRangeAssert(@Nullable TextRange actual) { + super(actual, TextRangeAssert.class); + } + + public TextRangeAssert hasRange(int startLine, int startLineOffset, int endLine, int endLineOffset) { + isNotNull(); + assertThat(actual.start().line()).isEqualTo(startLine); + assertThat(actual.start().lineOffset()).isEqualTo(startLineOffset); + assertThat(actual.end().line()).isEqualTo(endLine); + assertThat(actual.end().lineOffset()).isEqualTo(endLineOffset); + return this; + } + + public static TextRangeAssert assertTextRange(@Nullable TextRange actual) { + return new TextRangeAssert(actual); + } + +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/ThreadLocalLogTester.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/ThreadLocalLogTester.java new file mode 100644 index 00000000..b9d9066e --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/ThreadLocalLogTester.java @@ -0,0 +1,108 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +/** + * Only collect logs written on the same thead as the one used for the constructor of ThreadLocalLogTester + */ +public class ThreadLocalLogTester implements AfterEachCallback, BeforeEachCallback { + + private final ThreadLocalAppender appender = new ThreadLocalAppender(Thread.currentThread()); + private Level slf4jLevel = Level.DEBUG; + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + setLevel(slf4jLevel); + appender.start(); + getRootLogger().addAppender(appender); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + getRootLogger().detachAppender(appender); + appender.list.clear(); + appender.stop(); + } + + public Level getLevel() { + return slf4jLevel; + } + + public ThreadLocalLogTester setLevel(Level level) { + this.slf4jLevel = level; + this.appender.setLevel(level); + return this; + } + + public List logs() { + return appender.list.stream() + .map(ILoggingEvent::getFormattedMessage) + .collect(Collectors.toList()); + } + + public List logs(Level level) { + ch.qos.logback.classic.Level logBackLevel = ch.qos.logback.classic.Level.toLevel(level.toString()); + return appender.list.stream() + .filter(event -> event.getLevel().toInt() == logBackLevel.toInt()) + .map(ILoggingEvent::getFormattedMessage) + .collect(Collectors.toList()); + } + + public ThreadLocalLogTester clear() { + appender.list.clear(); + return this; + } + + private static ch.qos.logback.classic.Logger getRootLogger() { + return (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + } + + public static class ThreadLocalAppender extends ListAppender { + private final Thread filteringThread; + private ch.qos.logback.classic.Level logBackLevel; + public ThreadLocalAppender(Thread filteringThread) { + this.filteringThread = filteringThread; + } + + public void setLevel(Level level) { + this.logBackLevel = ch.qos.logback.classic.Level.toLevel(level.toString()); + if (!logBackLevel.isGreaterOrEqual(getRootLogger().getLevel())) { + getRootLogger().setLevel(logBackLevel); + } + } + + @Override + protected void append(ILoggingEvent e) { + if (Thread.currentThread() == filteringThread && e.getLevel().isGreaterOrEqual(logBackLevel)) { + super.append(e); + } + } + } +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/TreeAssert.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/TreeAssert.java new file mode 100644 index 00000000..59d550f2 --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/TreeAssert.java @@ -0,0 +1,218 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.Assertions; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.ParameterTree; +import org.sonarsource.slang.api.StringLiteralTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.UnaryExpressionTree; +import org.sonarsource.slang.utils.SyntacticEquivalence; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonarsource.slang.testing.RangeAssert.assertRange; +import static org.sonarsource.slang.visitors.TreePrinter.tree2string; + +public class TreeAssert extends AbstractAssert { + + public TreeAssert(Tree actual) { + super(actual, TreeAssert.class); + } + + public TreeAssert isIdentifier(String expectedName) { + isNotNull(); + isInstanceOf(IdentifierTree.class); + IdentifierTree actualIdentifier = (IdentifierTree) actual; + if (!Objects.equals(actualIdentifier.name(), expectedName)) { + failWithMessage("Expected identifier's name to be <%s> but was <%s>", expectedName, actualIdentifier.name()); + } + return this; + } + + public TreeAssert hasTokens(String... tokens) { + isNotNull(); + List expected = actual.metaData().tokens().stream().map(Token::text).collect(Collectors.toList()); + Assertions.assertThat(Arrays.asList(tokens)).isEqualTo(expected); + return this; + } + + public TreeAssert hasParameterName(String expectedIdentifierName) { + isNotNull(); + isInstanceOf(ParameterTree.class); + ParameterTree actualParameter = (ParameterTree) actual; + assertTree(actualParameter.identifier()).isIdentifier(expectedIdentifierName); + return this; + } + + public TreeAssert hasParameterNames(String... names) { + isNotNull(); + isInstanceOf(FunctionDeclarationTree.class); + FunctionDeclarationTree actualFunction = (FunctionDeclarationTree) actual; + List actualParameters = actualFunction.formalParameters().stream() + .filter(ParameterTree.class::isInstance) + .map(ParameterTree.class::cast) + .collect(Collectors.toList()); + if (actualParameters.size() != names.length) { + failWithMessage("Expected to have <%s> parameters but found <%s>", names.length, actualParameters.size()); + } + for (int i = 0; i < actualParameters.size(); i++) { + ParameterTree tree = actualParameters.get(i); + assertTree(tree).hasParameterName(names[i]); + } + return this; + } + + public TreeAssert isLiteral(String expected) { + isNotNull(); + isInstanceOf(LiteralTree.class); + LiteralTree actualLiteral = (LiteralTree) actual; + if (!Objects.equals(actualLiteral.value(), expected)) { + failWithMessage("Expected literal value to be <%s> but was <%s>", expected, actualLiteral.value()); + } + return this; + } + + public TreeAssert isUnaryExpression(UnaryExpressionTree.Operator expectedOperator) { + isNotNull(); + isInstanceOf(UnaryExpressionTree.class); + UnaryExpressionTree actualUnary = (UnaryExpressionTree) actual; + if (!Objects.equals(actualUnary.operator(), expectedOperator)) { + failWithMessage("Expected operator to be <%s> but was <%s>", expectedOperator, actualUnary.operator()); + } + return this; + } + + public TreeAssert isStringLiteral(String expected) { + isNotNull(); + isInstanceOf(StringLiteralTree.class); + StringLiteralTree actualLiteral = (StringLiteralTree) actual; + if (!Objects.equals(actualLiteral.content(), expected)) { + failWithMessage("Expected string content to be <%s> but was <%s>", expected, actualLiteral.content()); + } + return this; + } + + public TreeAssert isBinaryExpression(BinaryExpressionTree.Operator expectedOperator) { + isNotNull(); + isInstanceOf(BinaryExpressionTree.class); + BinaryExpressionTree actualBinary = (BinaryExpressionTree) actual; + if (!Objects.equals(actualBinary.operator(), expectedOperator)) { + failWithMessage("Expected operator to be <%s> but was <%s>", expectedOperator, actualBinary.operator()); + } + return this; + } + + public TreeAssert isAssignmentExpression(AssignmentExpressionTree.Operator expectedOperator) { + isNotNull(); + isInstanceOf(AssignmentExpressionTree.class); + AssignmentExpressionTree actualBinary = (AssignmentExpressionTree) actual; + if (!Objects.equals(actualBinary.operator(), expectedOperator)) { + failWithMessage("Expected assignment operator to be <%s> but was <%s>", expectedOperator, actualBinary.operator()); + } + return this; + } + + public TreeAssert isBlock(Class... classes) { + isNotNull(); + isInstanceOf(BlockTree.class); + hasChildren(classes); + return this; + } + + public TreeAssert hasChildren(Class... classes) { + hasChildren(classes.length); + for (int i = 0; i < actual.children().size(); i++) { + Tree tree = actual.children().get(i); + if (!classes[i].isAssignableFrom(tree.getClass())) { + failWithMessage("Expected to find instance of <%s> but was <%s>", classes[i], tree.getClass()); + } + } + return this; + } + + private boolean checkHasDescendant(Tree expected) { + isNotNull(); + return actual.descendants().anyMatch(descendant -> SyntacticEquivalence.areEquivalent(descendant, expected)); + } + + public TreeAssert hasDescendant(Tree expected) { + if (!checkHasDescendant(expected)) { + failWithMessage("Expected tree <%s> to be a descendant of <%s>", expected, actual); + } + return this; + } + + public TreeAssert hasNotDescendant(Tree expected) { + if (checkHasDescendant(expected)) { + failWithMessage("Expected tree <%s> not to be a descendant of <%s>", expected, actual); + } + return this; + } + + public TreeAssert hasChildren(int count) { + isNotNull(); + if (actual.children().size() != count) { + failWithMessage("Expected to have <%s> children elements but found <%s>", count, actual.children().size()); + } + return this; + } + + public TreeAssert hasTextRange(int startLine, int startLineOffset, int endLine, int endLineOffset) { + isNotNull(); + assertRange(actual.metaData().textRange()).hasRange(startLine, startLineOffset, endLine, endLineOffset); + return this; + } + + public TreeAssert isEquivalentTo(Tree expected) { + isNotNull(); + boolean equivalent = SyntacticEquivalence.areEquivalent(actual, expected); + if (!equivalent) { + assertThat(tree2string(actual)).isEqualTo(tree2string(expected)); + failWithMessage("Expected tree: <%s>\nbut was: <%s>", tree2string(expected), tree2string(actual)); + } + return this; + } + + public TreeAssert isNotEquivalentTo(Tree expected) { + isNotNull(); + boolean equivalent = SyntacticEquivalence.areEquivalent(actual, expected); + if (equivalent) { + failWithMessage("Expected <%s> to not be equivalent to <%s>", actual, expected); + } + return this; + } + + public static TreeAssert assertTree(Tree actual) { + return new TreeAssert(actual); + } + +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/TreesAssert.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/TreesAssert.java new file mode 100644 index 00000000..7e844dd8 --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/TreesAssert.java @@ -0,0 +1,60 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.utils.SyntacticEquivalence; +import java.util.List; +import org.assertj.core.api.AbstractAssert; + +import static org.sonarsource.slang.visitors.TreePrinter.tree2string; +import static org.assertj.core.api.Assertions.assertThat; + +public class TreesAssert extends AbstractAssert> { + + public TreesAssert(List actual) { + super(actual, TreesAssert.class); + } + + public static TreesAssert assertTrees(List actual) { + return new TreesAssert(actual); + } + + public TreesAssert isEquivalentTo(List expected) { + isNotNull(); + boolean equivalent = SyntacticEquivalence.areEquivalent(actual, expected); + if (!equivalent) { + assertThat(tree2string(actual)).isEqualTo(tree2string(expected)); + failWithMessage("Expected tree: <%s>\nbut was: <%s>", tree2string(expected), tree2string(actual)); + } + return this; + } + + public TreesAssert isNotEquivalentTo(List expected) { + isNotNull(); + boolean equivalent = SyntacticEquivalence.areEquivalent(actual, expected); + if (equivalent) { + assertThat(tree2string(actual)).isNotEqualTo(tree2string(expected)); + failWithMessage("Expected <%s> to not be equivalent to <%s>", actual, expected); + } + return this; + } + +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/Verifier.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/Verifier.java new file mode 100644 index 00000000..2042927c --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/Verifier.java @@ -0,0 +1,173 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.function.BiConsumer; +import javax.annotation.Nullable; + +import org.sonarsource.analyzer.commons.checks.verifier.SingleFileVerifier; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.HasTextRange; +import org.sonarsource.slang.api.TextPointer; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.checks.api.CheckContext; +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.visitors.TreeContext; +import org.sonarsource.slang.visitors.TreeVisitor; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public final class Verifier { + + private Verifier() { + // utility class + } + + public static void verify(ASTConverter converter, Path path, SlangCheck check) { + createVerifier(converter, path, check).assertOneOrMoreIssues(); + } + + public static void verifyNoIssue(ASTConverter converter, Path path, SlangCheck check) { + createVerifier(converter, path, check).assertNoIssues(); + } + + private static SingleFileVerifier createVerifier(ASTConverter converter, Path path, SlangCheck check) { + + SingleFileVerifier verifier = SingleFileVerifier.create(path, UTF_8); + + String testFileContent = readFile(path); + Tree root = converter.parse(testFileContent, null); + + ((TopLevelTree) root).allComments() + .forEach(comment -> { + TextPointer start = comment.textRange().start(); + verifier.addComment(start.line(), start.lineOffset()+1, comment.text(), 2, 0); + }); + + TestContext ctx = new TestContext(verifier, path.getFileName().toString(), testFileContent); + check.initialize(ctx); + ctx.scan(root); + + return verifier; + } + + private static String readFile(Path path) { + try { + return new String(Files.readAllBytes(path), UTF_8); + } catch (IOException e) { + throw new IllegalStateException("Cannot read " + path, e); + } + } + + private static class TestContext extends TreeContext implements InitContext, CheckContext { + + private final TreeVisitor visitor; + private final SingleFileVerifier verifier; + private final String filename; + private String testFileContent; + + public TestContext(SingleFileVerifier verifier, String filename, String testFileContent) { + this.verifier = verifier; + this.filename = filename; + this.testFileContent = testFileContent; + visitor = new TreeVisitor<>(); + } + + public void scan(@Nullable Tree root) { + visitor.scan(this, root); + } + + @Override + public void register(Class cls, BiConsumer consumer) { + visitor.register(cls, (ctx, node) -> consumer.accept(this, node)); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message) { + reportIssue(toHighlight, message, Collections.emptyList()); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message, SecondaryLocation secondaryLocation) { + reportIssue(toHighlight, message, Collections.singletonList(secondaryLocation)); + } + + @Override + public String filename() { + return filename; + } + + @Override + public String fileContent() { + return testFileContent; + } + + @Override + public void reportIssue(TextRange textRange, String message) { + reportIssue(textRange, message, Collections.emptyList(), null); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations) { + reportIssue(toHighlight, message, secondaryLocations, null); + } + + @Override + public void reportIssue(HasTextRange toHighlight, String message, List secondaryLocations, @Nullable Double gap) { + reportIssue(toHighlight.textRange(), message, secondaryLocations, gap); + } + + public void reportFileIssue(String message) { + reportFileIssue(message, null); + } + + @Override + public void reportFileIssue(String message, @Nullable Double gap) { + verifier.reportIssue(message).onFile().withGap(gap); + } + + private void reportIssue(TextRange textRange, String message, List secondaryLocations, @Nullable Double gap) { + TextPointer start = textRange.start(); + TextPointer end = textRange.end(); + SingleFileVerifier.Issue issue = verifier + .reportIssue(message) + .onRange(start.line(), start.lineOffset() + 1, end.line(), end.lineOffset()) + .withGap(gap); + secondaryLocations.forEach(secondary -> issue.addSecondary( + secondary.textRange.start().line(), + secondary.textRange.start().lineOffset() + 1, + secondary.textRange.end().line(), + secondary.textRange.end().lineOffset(), + secondary.message)); + } + + } + +} diff --git a/slang-testing/src/main/java/org/sonarsource/slang/testing/package-info.java b/slang-testing/src/main/java/org/sonarsource/slang/testing/package-info.java new file mode 100644 index 00000000..6e728f6e --- /dev/null +++ b/slang-testing/src/main/java/org/sonarsource/slang/testing/package-info.java @@ -0,0 +1,21 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@javax.annotation.ParametersAreNonnullByDefault +package org.sonarsource.slang.testing; diff --git a/slang-testing/src/test/java/org/sonarsource/slang/testing/AbstractSensorTestTest.java b/slang-testing/src/test/java/org/sonarsource/slang/testing/AbstractSensorTestTest.java new file mode 100644 index 00000000..d83af848 --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/slang/testing/AbstractSensorTestTest.java @@ -0,0 +1,103 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.util.Collection; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.ActiveRule; +import org.sonar.api.resources.Language; + +import static org.assertj.core.api.Assertions.assertThat; + +class AbstractSensorTestTest { + + private static final SensorTester SENSOR = new SensorTester(); + private static final Language LANGUAGE = new Language() { + @Override + public String getKey() { + return "dummyLang"; + } + + @Override + public String getName() { + return "Dummy"; + } + + @Override + public String[] getFileSuffixes() { + return new String[] {".dummy"}; + } + }; + + @BeforeEach + void setup(@TempDir File tmpBaseDir) throws Exception { + SENSOR.logTester.beforeEach(null); + SENSOR.setup(tmpBaseDir); + } + + @Test + void checkFactory_should_contain_rules() { + SENSOR.checkFactory("S1", "S2", "S3"); + Collection rules = SENSOR.context.activeRules().findAll(); + assertThat(rules) + .hasSize(3) + .allMatch(rule -> rule.ruleKey().rule().matches("S[1-3]")) + .allMatch(rule -> rule.ruleKey().repository().equals("myRepo")); + } + + @Test + void checkFactory_can_be_empty() { + SENSOR.checkFactory(); + Collection rules = SENSOR.context.activeRules().findAll(); + assertThat(rules).isEmpty(); + } + + @Test + void createImputFile_returns_input_file_with_same_content() throws Exception { + String content = "class A { }"; + String filename = "yolo.dummy"; + + InputFile inputFile = SENSOR.createInputFile(filename, content); + + assertThat(inputFile.contents()).isEqualTo(content); + assertThat(inputFile.charset()).isEqualTo(StandardCharsets.UTF_8); + assertThat(inputFile.language()).isEqualTo(LANGUAGE.getKey()); + assertThat(inputFile.type()).isEqualTo(InputFile.Type.MAIN); + assertThat(inputFile.filename()).isEqualTo(filename); + } + + private static class SensorTester extends AbstractSensorTest { + + @Override + protected String repositoryKey() { + return "myRepo"; + } + + @Override + protected Language language() { + return LANGUAGE; + } + } +} diff --git a/slang-testing/src/test/java/org/sonarsource/slang/testing/PackageScannerTest.java b/slang-testing/src/test/java/org/sonarsource/slang/testing/PackageScannerTest.java new file mode 100644 index 00000000..671e4622 --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/slang/testing/PackageScannerTest.java @@ -0,0 +1,35 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import java.util.List; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +class PackageScannerTest { + + @Test + void finds_classes_that_implement_slangcheck() { + List result = PackageScanner.findSlangChecksInPackage("org.sonarsource.test"); + assertThat(result).containsExactly("org.sonarsource.test.TestCheck"); + } + +} diff --git a/slang-testing/src/test/java/org/sonarsource/slang/testing/TextRangeAssertTest.java b/slang-testing/src/test/java/org/sonarsource/slang/testing/TextRangeAssertTest.java new file mode 100644 index 00000000..6c49a8c7 --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/slang/testing/TextRangeAssertTest.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.sonar.api.batch.fs.TextPointer; +import org.sonar.api.batch.fs.TextRange; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonarsource.slang.testing.TextRangeAssert.assertTextRange; + +class TextRangeAssertTest { + + private TextRange range; + + @BeforeEach + void setup() { + TextPointer start = mock(TextPointer.class); + when(start.line()).thenReturn(1); + when(start.lineOffset()).thenReturn(2); + + TextPointer end = mock(TextPointer.class); + when(end.line()).thenReturn(3); + when(end.lineOffset()).thenReturn(4); + + range = mock(TextRange.class); + when(range.start()).thenReturn(start); + when(range.end()).thenReturn(end); + } + + @Test + void range_ok() { + assertTextRange(range).hasRange(1, 2, 3, 4); + } + + @Test + void range_failure() { + assertThrows(AssertionError.class, + () -> assertTextRange(range).hasRange(1, 2, 3, 5)); + } + +} diff --git a/slang-testing/src/test/java/org/sonarsource/slang/testing/ThreadLocalLogTesterTest.java b/slang-testing/src/test/java/org/sonarsource/slang/testing/ThreadLocalLogTesterTest.java new file mode 100644 index 00000000..e46a8492 --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/slang/testing/ThreadLocalLogTesterTest.java @@ -0,0 +1,86 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; + +import static org.assertj.core.api.Assertions.assertThat; + +class ThreadLocalLogTesterTest { + + private static final Logger LOG = LoggerFactory.getLogger(ThreadLocalLogTesterTest.class); + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void logs() { + assertThat(logTester.getLevel()).isEqualTo(Level.DEBUG); + + logTester.setLevel(Level.TRACE); + assertThat(logTester.getLevel()).isEqualTo(Level.TRACE); + LOG.trace("BOOM in a {}.", "test"); + assertThat(logTester.logs()).containsExactly("BOOM in a test."); + logTester.clear(); + + logTester.setLevel(Level.DEBUG); + LOG.error("BOOM in a test."); + LOG.trace("ignored BOOM in a test."); + assertThat(logTester.logs()).containsExactly("BOOM in a test."); + logTester.clear(); + assertThat(logTester.logs()).isEmpty(); + + logTester.setLevel(Level.INFO); + LOG.error("BOOM in {} {}.", "a", "test"); + LOG.error("BOOM {} {} {}.", "in", "a", "test"); + LOG.error("BOOM in a test.", new RuntimeException("BOOM")); + assertThat(logTester.logs()).containsExactly("BOOM in a test.", "BOOM in a test.", "BOOM in a test."); + assertThat(logTester.logs(Level.ERROR)).hasSize(3); + assertThat(logTester.logs(Level.WARN)).isEmpty(); + logTester.clear(); + assertThat(logTester.logs()).isEmpty(); + } + + @Test + void ignore_logs_from_other_thead() throws InterruptedException { + LOG.error("BOOM in this thread #1"); + + ExecutorService executor = Executors.newFixedThreadPool(2); + executor.submit(() -> LOG.error("BOOM in other thread #1")); + LOG.error("BOOM in this thread #2"); + executor.submit(() -> LOG.error("BOOM in other thread #2")); + executor.shutdown(); + assertThat(executor.awaitTermination(1, TimeUnit.SECONDS)).isTrue(); + + LOG.error("BOOM in this thread #3"); + + assertThat(logTester.logs()).containsExactly( + "BOOM in this thread #1", + "BOOM in this thread #2", + "BOOM in this thread #3"); + } +} diff --git a/slang-testing/src/test/java/org/sonarsource/slang/testing/TreeAssertTest.java b/slang-testing/src/test/java/org/sonarsource/slang/testing/TreeAssertTest.java new file mode 100644 index 00000000..75f9fcbb --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/slang/testing/TreeAssertTest.java @@ -0,0 +1,299 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.Annotation; +import org.sonarsource.slang.api.AssignmentExpressionTree; +import org.sonarsource.slang.api.BinaryExpressionTree; +import org.sonarsource.slang.api.Comment; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.LiteralTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.TextRange; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.TreeMetaData; +import org.sonarsource.slang.impl.AssignmentExpressionTreeImpl; +import org.sonarsource.slang.impl.BinaryExpressionTreeImpl; +import org.sonarsource.slang.impl.BlockTreeImpl; +import org.sonarsource.slang.impl.FunctionDeclarationTreeImpl; +import org.sonarsource.slang.impl.IdentifierTreeImpl; +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.sonarsource.slang.impl.NativeTreeImpl; +import org.sonarsource.slang.impl.ParameterTreeImpl; +import org.sonarsource.slang.impl.StringLiteralTreeImpl; +import org.sonarsource.slang.impl.TextRangeImpl; +import org.sonarsource.slang.impl.TokenImpl; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.sonarsource.slang.testing.TreeAssert.assertTree; + +class TreeAssertTest { + + private static final IdentifierTreeImpl IDENTIFIER_ABC = new IdentifierTreeImpl(null, "abc"); + private static final StringLiteralTreeImpl STRING_LITERAL_STR = new StringLiteralTreeImpl(null, "\"str\""); + private static final LiteralTreeImpl LITERAL_42 = new LiteralTreeImpl(null, "42"); + public static final AssignmentExpressionTreeImpl ASSIGN_42_TO_ABC = new AssignmentExpressionTreeImpl(null, AssignmentExpressionTree.Operator.EQUAL, IDENTIFIER_ABC, LITERAL_42); + private static final BinaryExpressionTreeImpl ABC_PLUS_42 = new BinaryExpressionTreeImpl(null, BinaryExpressionTree.Operator.PLUS, null, IDENTIFIER_ABC, LITERAL_42); + private static final BinaryExpressionTreeImpl ABC_PLUS_ABC_PLUS_42 = new BinaryExpressionTreeImpl(null, BinaryExpressionTree.Operator.PLUS, null, IDENTIFIER_ABC, ABC_PLUS_42); + private static final ParameterTreeImpl PARAMETER_ABC = new ParameterTreeImpl(null, IDENTIFIER_ABC, null); + private static final FunctionDeclarationTreeImpl FUNCTION_ABC = new FunctionDeclarationTreeImpl(null, Collections.emptyList(), false, null, null, + singletonList(PARAMETER_ABC), null, emptyList()); + + @Test + void identifier_ok() { + assertTree(IDENTIFIER_ABC).isIdentifier("abc"); + } + + @Test + void has_tokens() { + Token token1 = new TokenImpl(new TextRangeImpl(1,0,1,1), "a", Token.Type.OTHER); + Token token2 = new TokenImpl(new TextRangeImpl(1,2,1,3), "b", Token.Type.OTHER); + TextRangeImpl textRange = new TextRangeImpl(token1.textRange().start(), token2.textRange().end()); + NativeTree nativeTree = new NativeTreeImpl(meta(textRange, token1, token2), null, Collections.emptyList()); + assertTree(nativeTree).hasTokens("a", "b"); + } + + @Test + void identifier_with_wrong_name() { + assertThrows(AssertionError.class, + () -> assertTree(IDENTIFIER_ABC).isIdentifier("xxx")); + } + + @Test + void not_an_identifier() { + assertThrows(AssertionError.class, + () -> assertTree(LITERAL_42).isIdentifier("abc")); + } + + @Test + void parameter_has_identifier() { + assertTree(PARAMETER_ABC).hasParameterName("abc"); + } + + @Test + void parameter_does_not_have_identifier() { + assertThrows(AssertionError.class, + () -> assertTree(PARAMETER_ABC).hasParameterName("xxx")); + } + + @Test + void function_has_parameters() { + assertTree(FUNCTION_ABC).hasParameterNames("abc"); + } + + @Test + void function_does_not_have_two_parameters() { + assertThrows(AssertionError.class, + () -> assertTree(FUNCTION_ABC).hasParameterNames("abc", "xxx")); + } + + @Test + void function_does_not_have_parameters() { + assertThrows(AssertionError.class, + () -> assertTree(FUNCTION_ABC).hasParameterNames("xxx")); + } + + @Test + void literal_ok() { + assertTree(LITERAL_42).isLiteral("42"); + } + + @Test + void literal_with_wrong_value() { + assertThrows(AssertionError.class, + () -> assertTree(LITERAL_42).isLiteral("123")); + } + + @Test + void not_a_literal() { + assertThrows(AssertionError.class, + () -> assertTree(new LiteralTreeImpl(null, "42")).isLiteral("123")); + } + + @Test + void string_literal_ok() { + assertTree(STRING_LITERAL_STR).isStringLiteral("str"); + } + + @Test + void string_literal_failure() { + assertThrows(AssertionError.class, + () -> assertTree(STRING_LITERAL_STR).isStringLiteral("abc")); + } + + @Test + void not_a_string_literal() { + assertThrows(AssertionError.class, + () -> assertTree(IDENTIFIER_ABC).isStringLiteral("abc")); + } + + @Test + void binary_ok() { + assertTree(ABC_PLUS_42).isBinaryExpression(BinaryExpressionTree.Operator.PLUS); + } + + @Test + void binary_with_wrong_operator() { + assertThrows(AssertionError.class, + () -> assertTree(ABC_PLUS_42).isBinaryExpression(BinaryExpressionTree.Operator.MINUS)); + } + + @Test + void not_a_binary() { + assertThrows(AssertionError.class, + () -> assertTree(LITERAL_42).isBinaryExpression(BinaryExpressionTree.Operator.PLUS)); + } + + @Test + void assignment_ok() { + assertTree(ASSIGN_42_TO_ABC).isAssignmentExpression(AssignmentExpressionTree.Operator.EQUAL); + } + + @Test + void assignment_with_wrong_operator() { + assertThrows(AssertionError.class, + () -> assertTree(ASSIGN_42_TO_ABC).isAssignmentExpression(AssignmentExpressionTree.Operator.PLUS_EQUAL)); + } + + @Test + void not_an_assignment() { + assertThrows(AssertionError.class, + () -> assertTree(LITERAL_42).isAssignmentExpression(AssignmentExpressionTree.Operator.EQUAL)); + } + + @Test + void empty_block() { + assertTree(new BlockTreeImpl(null, Collections.emptyList())).isBlock(); + } + + @Test + void non_empty_block() { + assertTree(new BlockTreeImpl(null, singletonList(LITERAL_42))).isBlock(LiteralTree.class); + } + + @Test + void block_with_wrong_child_class() { + assertThrows(AssertionError.class, + () -> assertTree(new BlockTreeImpl(null, singletonList(LITERAL_42))).isBlock(IdentifierTree.class)); + } + + @Test + void block_with_too_many_children() { + assertThrows(AssertionError.class, + () -> assertTree(new BlockTreeImpl(null, singletonList(LITERAL_42))).isBlock()); + } + + @Test + void not_a_block() { + assertThrows(AssertionError.class, + () -> assertTree(LITERAL_42).isBlock(LiteralTree.class)); + } + + @Test + void text_range() { + assertTree(new IdentifierTreeImpl(meta(new TextRangeImpl(1, 2, 3, 4)), "a")).hasTextRange(1, 2, 3, 4); + } + + @Test + void wrong_text_range() { + assertThrows(AssertionError.class, + () -> assertTree(new IdentifierTreeImpl(meta(new TextRangeImpl(1, 2, 3, 4)), "a")).hasTextRange(1, 2, 3, 42)); + } + + @Test + void equivalent_ok() { + assertTree(LITERAL_42).isEquivalentTo(new LiteralTreeImpl(null, "42")); + } + + @Test + void equivalent_failure() { + assertThrows(AssertionError.class, + () -> assertTree(LITERAL_42).isEquivalentTo(new LiteralTreeImpl(null, "43"))); + } + + @Test + void notequivalent_ok() { + assertTree(LITERAL_42).isNotEquivalentTo(new LiteralTreeImpl(null, "43")); + } + + @Test + void notequivalent_failure() { + assertThrows(AssertionError.class, + () -> assertTree(LITERAL_42).isNotEquivalentTo(new LiteralTreeImpl(null, "42"))); + } + + @Test + void hasdescendant_ok() { + assertTree(ABC_PLUS_ABC_PLUS_42).hasDescendant(new LiteralTreeImpl(null, "42")); + } + + @Test + void hasdescendant_failure() { + assertThrows(AssertionError.class, + () -> assertTree(ABC_PLUS_ABC_PLUS_42).hasDescendant(new LiteralTreeImpl(null, "43"))); + } + + @Test + void hasnotdescendant_ok() { + assertTree(ABC_PLUS_ABC_PLUS_42).hasNotDescendant(new LiteralTreeImpl(null, "43")); + } + + @Test + void hasnotdescendant_failure() { + assertThrows(AssertionError.class, + () -> assertTree(ABC_PLUS_ABC_PLUS_42).hasNotDescendant(new LiteralTreeImpl(null, "42"))); + } + + private TreeMetaData meta(TextRange textRange, Token... tokens) { + return new TreeMetaData() { + @Override + public TextRange textRange() { + return textRange; + } + + @Override + public List commentsInside() { + return null; + } + + @Override + public List annotations() { + return Collections.emptyList(); + } + + @Override + public List tokens() { + return Arrays.asList(tokens); + } + + @Override + public Set linesOfCode() { + return Collections.emptySet(); + } + }; + } +} diff --git a/slang-testing/src/test/java/org/sonarsource/slang/testing/TreesAssertTest.java b/slang-testing/src/test/java/org/sonarsource/slang/testing/TreesAssertTest.java new file mode 100644 index 00000000..e90041a9 --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/slang/testing/TreesAssertTest.java @@ -0,0 +1,55 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import org.sonarsource.slang.impl.LiteralTreeImpl; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.sonarsource.slang.testing.TreesAssert.assertTrees; +import static java.util.Collections.singletonList; + +class TreesAssertTest { + + private static final LiteralTreeImpl LITERAL_42 = new LiteralTreeImpl(null, "42"); + + @Test + void equivalent_ok() { + assertTrees(singletonList(LITERAL_42)).isEquivalentTo(singletonList(new LiteralTreeImpl(null, "42"))); + } + + @Test + void equivalent_failure() { + assertThrows(AssertionError.class, + () -> assertTrees(singletonList(LITERAL_42)).isEquivalentTo(singletonList(new LiteralTreeImpl(null, "43")))); + } + + @Test + void notequivalent_ok() { + assertTrees(singletonList(LITERAL_42)).isNotEquivalentTo(singletonList(new LiteralTreeImpl(null, "43"))); + } + + @Test + void notequivalent_failure() { + assertThrows(AssertionError.class, + () -> assertTrees(singletonList(LITERAL_42)).isNotEquivalentTo(singletonList(new LiteralTreeImpl(null, "42")))); + } + +} diff --git a/slang-testing/src/test/java/org/sonarsource/slang/testing/VerifierTest.java b/slang-testing/src/test/java/org/sonarsource/slang/testing/VerifierTest.java new file mode 100644 index 00000000..8fb91b11 --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/slang/testing/VerifierTest.java @@ -0,0 +1,135 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.slang.testing; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.ComparisonFailure; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.IdentifierTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.api.SecondaryLocation; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.persistence.JsonTree; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; + +class VerifierTest { + + private static final Path BASE_DIR = Paths.get("src", "test", "resources"); + + private static final SlangCheck NO_ISSUE_CHECK = init -> { + }; + + private static final SlangCheck ISSUE_ON_IDENTIFIERS_CHECK = init -> + init.register(IdentifierTree.class, (ctx, identifier) -> ctx.reportIssue(identifier, "primary")); + + private static final SlangCheck ISSUE_ON_IDENTIFIER_TEXT_RANGE_CHECK = init -> + init.register(IdentifierTree.class, (ctx, identifier) -> ctx.reportIssue(identifier.textRange(), "primary")); + + private static final SlangCheck ISSUE_WITH_SECONDARY_LOCATION_CHECK = init -> + init.register(TopLevelTree.class, (ctx, top) -> + ctx.reportIssue( + top.declarations().get(0), + "primary", + new SecondaryLocation(top.declarations().get(1), "secondary"))); + + private static final SlangCheck ISSUE_ON_FILE = init -> + init.register(TopLevelTree.class, (ctx, identifier) -> ctx.reportFileIssue(ctx.filename() + " length " + ctx.fileContent().length())); + + @Test + void verify_with_issue() throws IOException { + Path path = BASE_DIR.resolve("primary.code"); + ASTConverter converter = createConverter(path); + Verifier.verify(converter, path, ISSUE_ON_IDENTIFIERS_CHECK); + Verifier.verify(converter, path, ISSUE_ON_IDENTIFIER_TEXT_RANGE_CHECK); + + ComparisonFailure e = assertThrows(ComparisonFailure.class, + () -> Verifier.verifyNoIssue(converter, path, ISSUE_ON_IDENTIFIERS_CHECK)); + assertThat(e).hasMessageStartingWith("ERROR: 'assertNoIssues()' is called but there's some 'Noncompliant' comments. In file (primary.code:1)"); + } + + @Test + void verify_with_wrong_message() throws IOException { + Path path = BASE_DIR.resolve("wrong-message.code"); + ASTConverter converter = createConverter(path); + ComparisonFailure e = assertThrows(ComparisonFailure.class, + () -> Verifier.verify(converter, path, ISSUE_ON_IDENTIFIERS_CHECK)); + assertThat(e).hasMessageContaining("" + + "- 001: Noncompliant {{wrong message}}\n" + + "+ 001: Noncompliant {{primary}}"); + } + + @Test + void verify_with_primary_and_secondary() throws IOException { + Path path = BASE_DIR.resolve("primary-and-secondary.code"); + ASTConverter converter = createConverter(path); + Verifier.verify(converter, path, ISSUE_WITH_SECONDARY_LOCATION_CHECK); + + ComparisonFailure e = assertThrows(ComparisonFailure.class, + () -> Verifier.verifyNoIssue(converter, path, ISSUE_WITH_SECONDARY_LOCATION_CHECK)); + assertThat(e).hasMessageStartingWith("ERROR: 'assertNoIssues()' is called but there's some 'Noncompliant' comments. In file (primary-and-secondary.code:1)"); + } + + @Test + void verify_issue_on_file() throws IOException { + Path path = BASE_DIR.resolve("file-issue.code"); + ASTConverter converter = createConverter(path); + Verifier.verify(converter, path, ISSUE_ON_FILE); + + ComparisonFailure e = assertThrows(ComparisonFailure.class, + () -> Verifier.verifyNoIssue(converter, path, ISSUE_ON_FILE)); + assertThat(e).hasMessageStartingWith("ERROR: 'assertNoIssues()' is called but there's some 'Noncompliant' comments. In file (file-issue.code:0)"); + } + + @Test + void verify_without_issue() throws IOException { + Path path = BASE_DIR.resolve("no-issue.code"); + ASTConverter converter = createConverter(path); + Verifier.verifyNoIssue(converter, path, NO_ISSUE_CHECK); + + ComparisonFailure e = assertThrows(ComparisonFailure.class, + () -> Verifier.verify(converter, path, NO_ISSUE_CHECK)); + assertThat(e).hasMessageStartingWith("ERROR: 'assertOneOrMoreIssues()' is called but there's no 'Noncompliant' comments. In file (no-issue.code:1)"); + } + + @Test + void invalid_path() throws IOException { + Path path = BASE_DIR.resolve("invalid-path.code"); + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> Verifier.verify(mock(ASTConverter.class), path, ISSUE_ON_IDENTIFIERS_CHECK)); + assertThat(e).hasMessageContaining("Cannot read"); + } + + private static ASTConverter createConverter(Path path) throws IOException { + Path jsonPath = path.getParent().resolve(path.getFileName() + ".json"); + String code = new String(Files.readAllBytes(path), UTF_8); + Tree tree = JsonTree.fromJson(new String(Files.readAllBytes(jsonPath), UTF_8)); + return content -> code.equals(content) ? tree : null; + } + +} diff --git a/slang-testing/src/test/java/org/sonarsource/test/TestCheck.java b/slang-testing/src/test/java/org/sonarsource/test/TestCheck.java new file mode 100644 index 00000000..2e4e0802 --- /dev/null +++ b/slang-testing/src/test/java/org/sonarsource/test/TestCheck.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarsource.test; + +import org.sonarsource.slang.checks.api.InitContext; +import org.sonarsource.slang.checks.api.SlangCheck; + +// Used in the test for PackageScanner +class TestCheck implements SlangCheck { + @Override + public void initialize(InitContext init) { } +} diff --git a/slang-testing/src/test/resources/file-issue.code b/slang-testing/src/test/resources/file-issue.code new file mode 100644 index 00000000..3a2e076f --- /dev/null +++ b/slang-testing/src/test/resources/file-issue.code @@ -0,0 +1 @@ +// Noncompliant@0 {{file-issue.code length 48}} diff --git a/slang-testing/src/test/resources/file-issue.code.json b/slang-testing/src/test/resources/file-issue.code.json new file mode 100644 index 00000000..d9d00cb2 --- /dev/null +++ b/slang-testing/src/test/resources/file-issue.code.json @@ -0,0 +1,19 @@ +{ + "treeMetaData": { + "comments": [ + { + "text": "// Noncompliant@0 {{file-issue.code length 48}}", + "contentText": " Noncompliant@0 {{file-issue.code length 48}}", + "range": "1:0:1:47", + "contentRange": "1:2:1:47" + } + ], + "tokens": [] + }, + "tree": { + "@type": "TopLevel", + "metaData": "1:0:1:47", + "declarations": [], + "firstCpdToken": null + } +} diff --git a/slang-testing/src/test/resources/no-issue.code b/slang-testing/src/test/resources/no-issue.code new file mode 100644 index 00000000..257cc564 --- /dev/null +++ b/slang-testing/src/test/resources/no-issue.code @@ -0,0 +1 @@ +foo diff --git a/slang-testing/src/test/resources/no-issue.code.json b/slang-testing/src/test/resources/no-issue.code.json new file mode 100644 index 00000000..9036ea7f --- /dev/null +++ b/slang-testing/src/test/resources/no-issue.code.json @@ -0,0 +1,16 @@ +{ + "treeMetaData": { + "comments": [], + "tokens": [ + {"textRange": "1:0:1:3", "text": "foo", "type": "OTHER"} + ] + }, + "tree": { + "@type": "TopLevel", + "metaData": "1:0:1:3", + "declarations": [ + {"@type": "Identifier", "metaData": "1:0:1:3", "name": "foo"} + ], + "firstCpdToken": "1:0:1:3" + } +} diff --git a/slang-testing/src/test/resources/primary-and-secondary.code b/slang-testing/src/test/resources/primary-and-secondary.code new file mode 100644 index 00000000..f5dcd4a0 --- /dev/null +++ b/slang-testing/src/test/resources/primary-and-secondary.code @@ -0,0 +1,2 @@ + a b // Noncompliant {{primary}} +// ^ ^< {{secondary}} diff --git a/slang-testing/src/test/resources/primary-and-secondary.code.json b/slang-testing/src/test/resources/primary-and-secondary.code.json new file mode 100644 index 00000000..8fbc597a --- /dev/null +++ b/slang-testing/src/test/resources/primary-and-secondary.code.json @@ -0,0 +1,21 @@ +{ + "treeMetaData": { + "comments": [ + {"text": "// Noncompliant {{primary}}", "contentText": " Noncompliant {{primary}}", "range": "1:7:1:34", "contentRange": "1:9:1:34"}, + {"text": "// ^ ^< {{secondary}}", "contentText": " ^ ^< {{secondary}}", "range": "2:0:2:21", "contentRange": "2:2:2:21"} + ], + "tokens": [ + {"textRange": "1:3:1:4", "text": "a", "type": "OTHER"}, + {"textRange": "1:5:1:6", "text": "b", "type": "OTHER"} + ] + }, + "tree": { + "@type": "TopLevel", + "metaData": "1:0:2:21", + "declarations": [ + {"@type": "Identifier", "metaData": "1:3:1:4", "name": "a"}, + {"@type": "Identifier", "metaData": "1:5:1:6", "name": "b"} + ], + "firstCpdToken": "1:3:1:4" + } +} diff --git a/slang-testing/src/test/resources/primary.code b/slang-testing/src/test/resources/primary.code new file mode 100644 index 00000000..5bbac97a --- /dev/null +++ b/slang-testing/src/test/resources/primary.code @@ -0,0 +1 @@ +foo // Noncompliant diff --git a/slang-testing/src/test/resources/primary.code.json b/slang-testing/src/test/resources/primary.code.json new file mode 100644 index 00000000..0e12e0f5 --- /dev/null +++ b/slang-testing/src/test/resources/primary.code.json @@ -0,0 +1,18 @@ +{ + "treeMetaData": { + "comments": [ + {"text": "// Noncompliant", "contentText": " Noncompliant", "range": "1:4:1:19", "contentRange": "1:6:1:19"} + ], + "tokens": [ + {"textRange": "1:0:1:3", "text": "foo", "type": "OTHER"} + ] + }, + "tree": { + "@type": "TopLevel", + "metaData": "1:0:1:19", + "declarations": [ + {"@type": "Identifier", "metaData": "1:0:1:3", "name": "foo"} + ], + "firstCpdToken": "1:0:1:3" + } +} diff --git a/slang-testing/src/test/resources/wrong-message.code b/slang-testing/src/test/resources/wrong-message.code new file mode 100644 index 00000000..ba0dc502 --- /dev/null +++ b/slang-testing/src/test/resources/wrong-message.code @@ -0,0 +1 @@ +foo // Noncompliant {{wrong message}} diff --git a/slang-testing/src/test/resources/wrong-message.code.json b/slang-testing/src/test/resources/wrong-message.code.json new file mode 100644 index 00000000..99ee665a --- /dev/null +++ b/slang-testing/src/test/resources/wrong-message.code.json @@ -0,0 +1,18 @@ +{ + "treeMetaData": { + "comments": [ + {"text": "// Noncompliant {{wrong message}}", "contentText": " Noncompliant {{wrong message}}", "range": "1:4:1:37", "contentRange": "1:6:1:37"} + ], + "tokens": [ + {"textRange": "1:0:1:3", "text": "foo", "type": "OTHER"} + ] + }, + "tree": { + "@type": "TopLevel", + "metaData": "1:0:1:37", + "declarations": [ + {"@type": "Identifier", "metaData": "1:0:1:3", "name": "foo"} + ], + "firstCpdToken": "1:0:1:3" + } +} diff --git a/sonar-go-plugin/build.gradle b/sonar-go-plugin/build.gradle new file mode 100644 index 00000000..e57e92a1 --- /dev/null +++ b/sonar-go-plugin/build.gradle @@ -0,0 +1,105 @@ +plugins { + id 'com.github.johnrengelman.shadow' version '6.1.0' +} + +// require sonar-go-to-slang binaries to be build +shadowJar.dependsOn ':sonar-go-to-slang:build' +test.dependsOn ':sonar-go-to-slang:build' + +dependencies { + implementation project(':slang-api') + implementation project(':slang-checks') + implementation project(':slang-plugin') + implementation project(':checkstyle-import') + // dependency on sonar-go-to-slang binaries + runtimeOnly files(project(':sonar-go-to-slang').buildDir) + compileOnly 'org.sonarsource.api.plugin:sonar-plugin-api' + testImplementation 'org.sonarsource.sonarqube:sonar-plugin-api-impl' + testImplementation 'org.sonarsource.api.plugin:sonar-plugin-api-test-fixtures' + implementation 'org.sonarsource.analyzer-commons:sonar-analyzer-commons' + implementation 'com.eclipsesource.minimal-json:minimal-json' + testImplementation "org.junit.jupiter:junit-jupiter-api" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine" + testImplementation 'org.assertj:assertj-core' + testImplementation 'org.mockito:mockito-core' + testImplementation 'io.github.classgraph:classgraph' + testImplementation project(':slang-antlr') + testImplementation project(':slang-testing') + testImplementation 'org.awaitility:awaitility:3.1.0' +} + +tasks.withType(JavaCompile) { + // Prevent warning: Gradle 5.0 will ignore annotation processors + options.compilerArgs += [ "-proc:none" ] +} + +test { + testLogging { + exceptionFormat 'full' // log the full stack trace (default is the 1st line of the stack trace) + events "skipped", "failed" // verbose log for failed and skipped tests (by default the name of the tests are not logged) + } +} + +jar { + manifest { + def displayVersion = (project.buildNumber == null ? project.version : project.version.substring(0, project.version.lastIndexOf('.')) + " (build ${project.buildNumber})") + def buildDate = new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") + attributes( + 'Build-Time': buildDate, + 'Implementation-Build': 'git rev-parse HEAD'.execute().text.trim(), + 'Plugin-BuildDate': buildDate, + 'Plugin-ChildFirstClassLoader': 'false', + 'Plugin-Class': 'org.sonar.go.plugin.GoPlugin', + 'Plugin-Description': 'Code Analyzer for Go', + 'Plugin-Developers': 'SonarSource Team', + 'Plugin-Display-Version': displayVersion, + 'Plugin-Homepage': 'http://redirect.sonarsource.com/plugins/go.html', + 'Plugin-IssueTrackerUrl': 'https://jira.sonarsource.com/browse/SONARSLANG', + 'Plugin-Key': 'go', + 'Plugin-License': 'GNU LGPL 3', + 'Plugin-Name': 'Go Code Quality and Security', + 'Plugin-Organization': 'SonarSource', + 'Plugin-OrganizationUrl': 'http://www.sonarsource.com', + 'Plugin-SourcesUrl': 'https://github.com/SonarSource/slang', + 'Plugin-Version': project.version, + 'Plugin-RequiredForLanguages': 'go', + 'Sonar-Version': '6.7', + 'SonarLint-Supported': 'true', + 'Version': "${project.version}", + 'Jre-Min-Version': '11', + ) + } +} + +shadowJar { + minimize { } + dependencies { + exclude(dependency('org.sonarsource.api.plugin:sonar-plugin-api')) + exclude(dependency('org.codehaus.woodstox:.*')) + exclude(dependency('org.codehaus.staxmate:.*')) + exclude(dependency('com.google.code.findbugs:jsr305')) + + exclude 'libs/**' + exclude 'META-INF/maven/**' + exclude 'tmp/**' + } + doLast { + enforceJarSizeAndCheckContent(shadowJar.archiveFile.get().asFile, 7_800_000L, 8_400_000L) + } +} + +artifacts { + archives shadowJar +} + +artifactoryPublish.skip = false + +publishing { + publications { + mavenJava(MavenPublication) { + artifact source: shadowJar, classifier: null + artifact sourcesJar + artifact javadocJar + } + } +} diff --git a/sonar-go-plugin/sonarpedia.json b/sonar-go-plugin/sonarpedia.json new file mode 100644 index 00000000..864972e4 --- /dev/null +++ b/sonar-go-plugin/sonarpedia.json @@ -0,0 +1,11 @@ +{ + "rules-metadata-path": "./src/main/resources/org/sonar/l10n/go/rules/go", + "languages": [ + "GO" + ], + "latest-update": "2023-10-02T13:48:50.744875Z", + "options": { + "no-language-in-filenames": true, + "preserve-filenames": true + } +} \ No newline at end of file diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/checks/CodeAfterJumpGoCheck.java b/sonar-go-plugin/src/main/java/org/sonar/go/checks/CodeAfterJumpGoCheck.java new file mode 100644 index 00000000..1305b3c7 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/checks/CodeAfterJumpGoCheck.java @@ -0,0 +1,43 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +import org.sonar.check.Rule; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.CodeAfterJumpCheck; + +import static org.sonar.go.checks.NativeKinds.LABEL; +import static org.sonar.go.checks.NativeKinds.SEMICOLON; + +@Rule(key = "S1763") +public class CodeAfterJumpGoCheck extends CodeAfterJumpCheck { + @Override + protected boolean isValidAfterJump(Tree tree) { + return tree instanceof NativeTree && + ((NativeTree) tree).nativeKind().toString().contains(LABEL); + } + + @Override + protected boolean shouldIgnore(Tree tree) { + return tree instanceof NativeTree && + ((NativeTree) tree).nativeKind().toString().equals(SEMICOLON); + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/checks/DuplicateBranchGoCheck.java b/sonar-go-plugin/src/main/java/org/sonar/go/checks/DuplicateBranchGoCheck.java new file mode 100644 index 00000000..43bb65e2 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/checks/DuplicateBranchGoCheck.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +import java.util.List; +import java.util.stream.Collectors; +import org.sonar.check.Rule; +import org.sonarsource.slang.api.MatchTree; +import org.sonarsource.slang.api.Token; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.DuplicateBranchCheck; +import org.sonarsource.slang.checks.api.CheckContext; + +@Rule(key = "S1871") +public class DuplicateBranchGoCheck extends DuplicateBranchCheck { + @Override + protected void checkConditionalStructure(CheckContext ctx, Tree tree, ConditionalStructure conditional) { + /* + If we enter a type switch, we may find branches with similar ASTs but different semantics. + In this case, we stop exploring the conditional structure to avoid raising FPs. + */ + if (tree instanceof MatchTree && isTypeSwitch((MatchTree) tree)) { + return; + } + super.checkConditionalStructure(ctx, tree, conditional); + } + + private static boolean isTypeSwitch(MatchTree matchTree) { + Tree expression = matchTree.expression(); + return expression != null && endsWithTypeSwitchGuard(expression); + } + + private static boolean endsWithTypeSwitchGuard(Tree matchTreeExpression) { + List tokens = matchTreeExpression.metaData().tokens(); + int size = tokens.size(); + return size >= 4 && tokens.subList(size - 4, size).stream() + .map(Token::text) + .collect(Collectors.joining("")).equals(".(type)"); + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/checks/NativeKinds.java b/sonar-go-plugin/src/main/java/org/sonar/go/checks/NativeKinds.java new file mode 100644 index 00000000..6c46d967 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/checks/NativeKinds.java @@ -0,0 +1,28 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +public final class NativeKinds { + public static final String LABEL = "LabeledStmt"; + public static final String SEMICOLON = "Semicolon"; + + private NativeKinds() { + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/checks/OneStatementPerLineGoCheck.java b/sonar-go-plugin/src/main/java/org/sonar/go/checks/OneStatementPerLineGoCheck.java new file mode 100644 index 00000000..9970e59a --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/checks/OneStatementPerLineGoCheck.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +import org.sonar.check.Rule; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.checks.OneStatementPerLineCheck; + +import static org.sonar.go.checks.NativeKinds.SEMICOLON; + +@Rule(key = "S122") +public class OneStatementPerLineGoCheck extends OneStatementPerLineCheck { + @Override + protected boolean shouldIgnore(Tree tree) { + return tree instanceof NativeTree && + ((NativeTree) tree).nativeKind().toString().equals(SEMICOLON); + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/checks/package-info.java b/sonar-go-plugin/src/main/java/org/sonar/go/checks/package-info.java new file mode 100644 index 00000000..b5270e07 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/checks/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.go.checks; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/converter/ExternalProcessStreamConsumer.java b/sonar-go-plugin/src/main/java/org/sonar/go/converter/ExternalProcessStreamConsumer.java new file mode 100644 index 00000000..749ecb18 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/converter/ExternalProcessStreamConsumer.java @@ -0,0 +1,70 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.converter; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +class ExternalProcessStreamConsumer { + + private static final Logger LOG = LoggerFactory.getLogger(ExternalProcessStreamConsumer.class); + private ExecutorService executorService; + + public ExternalProcessStreamConsumer() { + executorService = Executors.newCachedThreadPool(r -> { + Thread thread = new Thread(r); + thread.setName("stream-consumer"); + thread.setDaemon(true); + return thread; + }); + } + + public final void consumeStream(InputStream inputStream, StreamConsumer streamConsumer) { + executorService.submit(() -> { + try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { + readErrors(errorReader, streamConsumer); + } catch (IOException e) { + LOG.error("Error while reading stream", e); + } + }); + } + + protected void readErrors(BufferedReader errorReader, StreamConsumer streamConsumer) { + errorReader.lines().forEach(streamConsumer::consumeLine); + streamConsumer.finished(); + } + + interface StreamConsumer { + + void consumeLine(String line); + + default void finished() { + + } + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/converter/GoConverter.java b/sonar-go-plugin/src/main/java/org/sonar/go/converter/GoConverter.java new file mode 100644 index 00000000..c6c8732e --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/converter/GoConverter.java @@ -0,0 +1,176 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.converter; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.TimeUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.ParseException; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.persistence.JsonTree; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class GoConverter implements ASTConverter { + + private static final long MAX_SUPPORTED_SOURCE_FILE_SIZE = 1_500_000L; + private static final Logger LOG = LoggerFactory.getLogger(GoConverter.class); + private static final long PROCESS_TIMEOUT_MS = 5_000; + + private final ProcessBuilder processBuilder; + private final ExternalProcessStreamConsumer errorConsumer; + + public GoConverter(File workDir) { + this(new DefaultCommand(workDir)); + } + + GoConverter(Command command) { + processBuilder = new ProcessBuilder(command.getCommand()); + errorConsumer = new ExternalProcessStreamConsumer(); + } + + @Override + public Tree parse(String content) { + if (content.length() > MAX_SUPPORTED_SOURCE_FILE_SIZE) { + throw new ParseException("The file size is too big and should be excluded," + + " its size is " + content.length() + " (maximum allowed is " + MAX_SUPPORTED_SOURCE_FILE_SIZE + " bytes)"); + } + try { + return JsonTree.fromJson(executeGoToJsonProcess(content)); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new ParseException("Go parser external process interrupted: " + e.getMessage(), null, e); + } catch (IOException e) { + throw new ParseException(e.getMessage(), null, e); + } + } + + private String executeGoToJsonProcess(String content) throws IOException, InterruptedException { + Process process = processBuilder.start(); + errorConsumer.consumeStream(process.getErrorStream(), LOG::debug); + try (OutputStream out = process.getOutputStream()) { + out.write(content.getBytes(UTF_8)); + } + String output; + try (InputStream in = process.getInputStream()) { + output = readAsString(in); + } + boolean exited = process.waitFor(PROCESS_TIMEOUT_MS, TimeUnit.MILLISECONDS); + if (exited && process.exitValue() != 0) { + throw new ParseException("Go parser external process returned non-zero exit value: " + process.exitValue()); + } + if (process.isAlive()) { + process.destroyForcibly(); + throw new ParseException("Go parser external process took too long. External process killed forcibly"); + } + return output; + } + + private static String readAsString(InputStream in) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + copy(in, outputStream); + return new String(outputStream.toByteArray(), UTF_8); + } + + private static void copy(InputStream in, OutputStream out) throws IOException { + byte[] buffer = new byte[8192]; + int read; + while ((read = in.read(buffer)) >= 0) { + out.write(buffer, 0, read); + } + } + + interface Command { + List getCommand(); + } + + static class DefaultCommand implements Command { + + private final String command; + + DefaultCommand(File workDir) { + try { + command = extract(workDir); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + @Override + public List getCommand() { + return Arrays.asList(command, "-"); + } + + private static String extract(File workDir) throws IOException { + String executable = getExecutableForCurrentOS(System.getProperty("os.name"), System.getProperty("os.arch")); + byte[] executableData = getBytesFromResource(executable); + File dest = new File(workDir, executable); + if (!fileMatch(dest, executableData)) { + Files.write(dest.toPath(), executableData); + dest.setExecutable(true); + } + return dest.getAbsolutePath(); + } + + static boolean fileMatch(File dest, byte[] expectedContent) throws IOException { + if (!dest.exists()) { + return false; + } + byte[] actualContent = Files.readAllBytes(dest.toPath()); + return Arrays.equals(actualContent, expectedContent); + } + + static byte[] getBytesFromResource(String executable) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try(InputStream in = GoConverter.class.getClassLoader().getResourceAsStream(executable)) { + if (in == null) { + throw new IllegalStateException(executable + " binary not found on class path"); + } + copy(in, out); + } + return out.toByteArray(); + } + + static String getExecutableForCurrentOS(String osName, String arch) { + String os = osName.toLowerCase(Locale.ROOT); + if (os.contains("win")) { + return "sonar-go-to-slang-windows-amd64.exe"; + } else if (os.contains("mac")) { + if (arch.equals("aarch64")) { + return "sonar-go-to-slang-darwin-arm64"; + } else { + return "sonar-go-to-slang-darwin-amd64"; + } + } else { + return "sonar-go-to-slang-linux-amd64"; + } + } + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/converter/package-info.java b/sonar-go-plugin/src/main/java/org/sonar/go/converter/package-info.java new file mode 100644 index 00000000..61d2cb46 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/converter/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarSource SLang + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.go.converter; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/coverage/GoCoverSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/coverage/GoCoverSensor.java new file mode 100644 index 00000000..1053917f --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/coverage/GoCoverSensor.java @@ -0,0 +1,354 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.coverage; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.batch.sensor.coverage.NewCoverage; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.WildcardPattern; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public class GoCoverSensor implements Sensor { + + private static final Logger LOG = LoggerFactory.getLogger(GoCoverSensor.class); + private static final String GO_LANGUAGE_KEY = "go"; + + public static final String REPORT_PATH_KEY = "sonar.go.coverage.reportPaths"; + + // See ParseProfiles function: + // https://github.com/golang/go/blob/master/src/cmd/cover/profile.go + static final Pattern MODE_LINE_REGEXP = Pattern.compile("^mode: (\\w+)$"); + static final Pattern COVERAGE_LINE_REGEXP = Pattern.compile("^(.+):(\\d+)\\.(\\d+),(\\d+)\\.(\\d+) (\\d+) (\\d+)$"); + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor + .onlyOnLanguage(GO_LANGUAGE_KEY) + .onlyWhenConfiguration(conf -> conf.hasKey(REPORT_PATH_KEY)) + .name("Go Cover sensor for Go coverage"); + } + + @Override + public void execute(SensorContext context) { + execute(context, GoPathContext.DEFAULT); + } + + void execute(SensorContext context, GoPathContext goContext) { + try { + Coverage coverage = new Coverage(goContext); + getReportPaths(context).forEach(reportPath -> parse(reportPath, coverage)); + coverage.fileMap.forEach((filePath, coverageStats) -> { + try { + saveFileCoverage(context, filePath, coverageStats); + } catch (Exception e) { + LOG.error("Error saving coverage info for file " + filePath, e); + } + }); + } catch (Exception e) { + LOG.error("Coverage import failed: {}", e.getMessage(), e); + } + } + + private static void saveFileCoverage(SensorContext sensorContext, String filePath, List coverageStats) throws IOException { + FileSystem fileSystem = sensorContext.fileSystem(); + InputFile inputFile = findInputFile(filePath, fileSystem); + if (inputFile != null) { + LOG.debug("Saving coverage measures for file '{}'", filePath); + List lines = Arrays.asList(inputFile.contents().split("\\r?\\n")); + NewCoverage newCoverage = sensorContext.newCoverage().onFile(inputFile); + FileCoverage fileCoverage = new FileCoverage(coverageStats, lines); + for (Map.Entry entry : fileCoverage.lineMap.entrySet()) { + newCoverage.lineHits(entry.getKey(), entry.getValue().hits); + } + newCoverage.save(); + } else { + LOG.warn("File '{}' is not included in the project, ignoring coverage", filePath); + } + } + + /** + * It is possible that absolutePath references a file that does not exist in the file system. + * It happens when go tests where executed on a different computer. + * Even when absolute path does not match a file of the project, this method try to find a valid + * mach using a shorter relative path. + * @see sonar-go/issues/218 + */ + private static InputFile findInputFile(String absolutePath, FileSystem fileSystem) { + FilePredicates predicates = fileSystem.predicates(); + InputFile inputFile = fileSystem.inputFile(predicates.hasAbsolutePath(absolutePath)); + if (inputFile != null) { + return inputFile; + } + LOG.debug("Resolving file {} using relative path", absolutePath); + Path path = Paths.get(absolutePath); + inputFile = fileSystem.inputFile(predicates.hasRelativePath(path.toString())); + while (inputFile == null && path.getNameCount() > 1) { + path = path.subpath(1, path.getNameCount()); + inputFile = fileSystem.inputFile(predicates.hasRelativePath(path.toString())); + } + return inputFile; + } + + static Stream getReportPaths(SensorContext sensorContext) { + Configuration config = sensorContext.config(); + Path baseDir = sensorContext.fileSystem().baseDir().toPath(); + String[] reportPaths = config.getStringArray(REPORT_PATH_KEY); + return Arrays.stream(reportPaths).flatMap(reportPath -> + isWildcard(reportPath) + ? getPatternPaths(baseDir, reportPath) + : getRegularPath(baseDir, reportPath)); + } + + private static Stream getRegularPath(Path baseDir, String reportPath) { + Path path = Paths.get(reportPath); + if (!path.isAbsolute()) { + path = baseDir.resolve(path); + } + if (path.toFile().exists()) { + return Stream.of(path); + } + + LOG.error("Coverage report can't be loaded, report file not found, ignoring this file {}.", reportPath); + return Stream.empty(); + } + + private static boolean isWildcard(String path) { + return path.contains("*") || path.contains("?"); + } + + private static Stream getPatternPaths(Path baseDir, String reportPath) { + try (Stream paths = Files.walk(baseDir, 999)) { + return findMatchingPaths(baseDir, reportPath, paths); + + } catch (IOException e) { + LOG.error("Error finding coverage files using pattern {}", reportPath); + return Stream.empty(); + } + } + + private static String toUnixLikePath(String path) { + return path.replace('\\', '/'); + } + + private static Stream findMatchingPaths(Path baseDir, String reportPath, Stream paths) { + WildcardPattern globPattern = WildcardPattern.create(toUnixLikePath(reportPath)); + + List matchingPaths = paths + .filter(currentPath -> { + Path normalizedPath = baseDir.toAbsolutePath().relativize(currentPath.toAbsolutePath()); + String pathToMatch = toUnixLikePath(normalizedPath.toString()); + return globPattern.match(pathToMatch); + }) + .collect(Collectors.toList()); + + if (matchingPaths.isEmpty()) { + LOG.error("Coverage report can't be loaded, file(s) not found for pattern: '{}', ignoring this file.", reportPath); + } + + return matchingPaths.stream(); + } + + static void parse(Path reportPath, Coverage coverage) { + LOG.info("Load coverage report from '{}'", reportPath); + try (InputStream input = new FileInputStream(reportPath.toFile())) { + Scanner scanner = new Scanner(input, UTF_8.name()); + if (!scanner.hasNextLine() || !MODE_LINE_REGEXP.matcher(scanner.nextLine()).matches()) { + throw new IOException("Invalid go coverage, expect 'mode:' on the first line."); + } + int lineNumber = 2; + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (!line.isEmpty()) { + addIfValidLine(line, lineNumber, coverage); + } + lineNumber++; + } + } catch (IOException e) { + LOG.error("Error parsing coverage info for file {}: {}", reportPath, e.getMessage()); + } + } + + private static void addIfValidLine(String line, int lineNumber, Coverage coverage) { + try { + coverage.add(new CoverageStat(lineNumber, line)); + } catch (IllegalArgumentException e) { + LOG.debug("Ignoring line in coverage report: {}.", e.getMessage(), e); + } + } + + static class Coverage { + final GoPathContext goContext; + Map> fileMap = new HashMap<>(); + + Coverage(GoPathContext goContext) { + this.goContext = goContext; + } + + void add(CoverageStat coverage) { + fileMap + .computeIfAbsent(goContext.resolve(coverage.filePath), key -> new ArrayList<>()) + .add(coverage); + } + } + + static class FileCoverage { + Map lineMap = new HashMap<>(); + List lines; + + public FileCoverage(List coverageStats, @Nullable List lines) { + this.lines = lines; + coverageStats.forEach(this::add); + } + + private void add(CoverageStat coverage) { + int startLine = findStartIgnoringBrace(coverage); + int endLine = findEndIgnoringBrace(coverage, startLine); + for (int line = startLine; line <= endLine; line++) { + if (!isEmpty(line - 1)) { + lineMap.computeIfAbsent(line, key -> new LineCoverage()) + .add(coverage); + } + } + } + + private boolean isEmpty(int line) { + return lines != null && + lines.get(line).trim().isEmpty(); + } + + int findStartIgnoringBrace(CoverageStat coverage) { + int line = coverage.startLine; + int column = coverage.startCol; + while (shouldIgnore(line, column)) { + column++; + if (column > lines.get(line - 1).length()) { + line++; + column = 1; + } + } + return line; + } + + int findEndIgnoringBrace(CoverageStat coverage, int startLine) { + int line = coverage.endLine; + int column = coverage.endCol - 1; + if (lines != null && line > lines.size()) { + line = lines.size(); + column = lines.get(line - 1).length(); + } + while (line > startLine && shouldIgnore(line, column)) { + column--; + if (column == 0) { + line--; + column = lines.get(line - 1).length(); + } + } + return line; + } + + boolean shouldIgnore(int line, int column) { + if (lines != null && line > 0 && line <= lines.size() && column > 0) { + String currentLine = lines.get(line - 1); + if (column > currentLine.length()) { + // Ignore end of line + return true; + } + int ch = currentLine.charAt(column - 1); + return ch < ' ' || ch == '{' || ch == '}'; + } + return false; + } + } + + static class LineCoverage { + int hits = 0; + + void add(CoverageStat coverage) { + long sum = ((long) hits) + coverage.count; + if (sum > Integer.MAX_VALUE) { + hits = Integer.MAX_VALUE; + } else { + hits = (int) sum; + } + } + } + + static class CoverageStat { + + final String filePath; + final int startLine; + final int startCol; + final int endLine; + final int endCol; + final int count; + + CoverageStat(int lineNumber, String line) { + Matcher matcher = COVERAGE_LINE_REGEXP.matcher(line); + if (!matcher.matches()) { + throw new IllegalArgumentException("Invalid go coverage at line " + lineNumber); + } + filePath = matcher.group(1); + startLine = Integer.parseInt(matcher.group(2)); + startCol = Integer.parseInt(matcher.group(3)); + endLine = Integer.parseInt(matcher.group(4)); + endCol = Integer.parseInt(matcher.group(5)); + // No need to parse numStmt as it is never used. + count = parseIntWithOverflow(matcher.group(7)); + } + + private static int parseIntWithOverflow(String s) { + int result = 0; + try { + result = Integer.parseInt(s); + } catch (NumberFormatException e) { + // Thanks to the regex, we know that the input can only contain digits, the only possible Exception is therefore coming from overflow. + return Integer.MAX_VALUE; + } + return result; + } + + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/coverage/GoPathContext.java b/sonar-go-plugin/src/main/java/org/sonar/go/coverage/GoPathContext.java new file mode 100644 index 00000000..16804cbc --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/coverage/GoPathContext.java @@ -0,0 +1,125 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.coverage; + +import java.io.File; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.sonar.api.batch.fs.FileSystem; + +public class GoPathContext { + private static final String LINUX_ABSOLUTE_OLD_PREFIX = "_/"; + private static final String WINDOWS_ABSOLUTE_OLD_PREFIX = "_\\"; + private static final Pattern WINDOWS_ABSOLUTE_OLD_REGEX = Pattern.compile("^_\\\\(\\w)_\\\\"); + private static final int MAX_PATH_CACHE_SIZE = 100; + + public static final GoPathContext DEFAULT = new GoPathContext(File.separatorChar, File.pathSeparator, System.getenv("GOPATH")); + final char fileSeparator; + final List goSrcPathList; + final Map resolvedPaths = new LinkedHashMap() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > MAX_PATH_CACHE_SIZE; + } + }; + + public GoPathContext(char fileSeparator, String pathSeparator, @Nullable String goPath) { + this.fileSeparator = fileSeparator; + List goPathEntries = Collections.emptyList(); + if (goPath != null) { + goPathEntries = Arrays.asList(goPath.split(pathSeparator)); + } + this.goSrcPathList = goPathEntries.stream() + .filter(path -> !path.isEmpty()) + .map(path -> concat(path, "src")) + .collect(Collectors.toList()); + } + + String concat(String parentPath, String childPath) { + if (parentPath.isEmpty() || parentPath.charAt(parentPath.length() - 1) == fileSeparator) { + return parentPath + childPath; + } + return parentPath + fileSeparator + childPath; + } + + /** + * Try to resolve the absolute path of the given filePath. If filePath is absolute + * (start with _) return the absolute path without the '_'. If filePath is relative, + * try to append the first GOPATH entry where this file exists, otherwise return + * a non-existing absolute path using the first GOPATH entry (or just filePath itself + * if GOPATH is empty). + * See {@link GoCoverSensor#findInputFile(String, FileSystem)} + */ + public String resolve(String filePath) { + return resolvedPaths.computeIfAbsent(filePath, path -> + getAbsolutePathForOldGoVersions(path) + .orElseGet(() -> getAbsolutePath(path) + .orElseGet(() -> prefixByFirstValidGoPath(path) + .orElseGet(() -> prefixByFirstGoPath(path))))); + } + + /** + * Old go versions, for projects outside GOPATH, prefix the absolute path with '_'. + * Newer versions (tested using 1.11.4 and later) do not use the prefix anymore. + * e.g. + * unix: "_/mnt/c/src..." vs "/mnt/c/src..." + * windows: "_\c_\src..." vs "c:\src..." + */ + private static Optional getAbsolutePathForOldGoVersions(String path){ + if (path.startsWith(LINUX_ABSOLUTE_OLD_PREFIX)) { + return Optional.of(path.substring(1)); + } else if (path.startsWith(WINDOWS_ABSOLUTE_OLD_PREFIX)) { + Matcher matcher = WINDOWS_ABSOLUTE_OLD_REGEX.matcher(path); + if (matcher.find()) { + matcher.reset(); + return Optional.of(matcher.replaceFirst("$1:\\\\")); + } + } + return Optional.empty(); + } + + private static Optional getAbsolutePath(String path) { + return Paths.get(path).isAbsolute() ? Optional.of(path) : Optional.empty(); + } + + private Optional prefixByFirstValidGoPath(String filePath) { + return goSrcPathList.stream() + .map(goPath -> concat(goPath, filePath)) + .filter(path -> new File(path).exists()) + .findFirst(); + } + + private String prefixByFirstGoPath(String filePath) { + if (goSrcPathList.isEmpty()) { + return filePath; + } + return concat(goSrcPathList.get(0), filePath); + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/coverage/package-info.java b/sonar-go-plugin/src/main/java/org/sonar/go/coverage/package-info.java new file mode 100644 index 00000000..93211db4 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/coverage/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube Go Plugin + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.go.coverage; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/AbstractReportSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/AbstractReportSensor.java new file mode 100644 index 00000000..0b7f0376 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/AbstractReportSensor.java @@ -0,0 +1,158 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonArray; +import com.eclipsesource.json.JsonObject; +import com.eclipsesource.json.JsonValue; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.function.Consumer; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.issue.NewExternalIssue; +import org.sonar.api.batch.sensor.issue.NewIssueLocation; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.rule.RulesDefinition.Context; +import org.sonar.api.server.rule.RulesDefinition.NewRepository; +import org.sonar.api.server.rule.RulesDefinition.NewRule; +import org.sonar.go.plugin.GoLanguage; +import org.sonarsource.slang.plugin.AbstractPropertyHandlerSensor; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.sonarsource.slang.utils.LogArg.lazyArg; + +public abstract class AbstractReportSensor extends AbstractPropertyHandlerSensor { + + private static final Logger LOG = LoggerFactory.getLogger(AbstractReportSensor.class); + + static final long DEFAULT_REMEDIATION_COST = 5L; + static final Severity DEFAULT_SEVERITY = Severity.MAJOR; + static final String GENERIC_ISSUE_KEY = "issue"; + + protected AbstractReportSensor(AnalysisWarnings analysisWarnings, String propertyKey, String propertyName, String configurationkey) { + super(analysisWarnings, propertyKey, propertyName, configurationkey, GoLanguage.KEY); + } + + @Nullable + abstract ExternalIssue parse(String line); + + @Override + public Consumer reportConsumer(SensorContext context) { + return file -> importReport(context, file); + } + + protected String logPrefix() { + return this.getClass().getSimpleName() + ": "; + } + + private void importReport(SensorContext context, File report) { + try { + for (String line : Files.readAllLines(report.toPath(), UTF_8)) { + if (!line.isEmpty()) { + ExternalIssue issue = parse(line); + if (issue != null) { + addLineIssue(context, issue); + } + } + } + } catch (IOException e) { + LOG.error("{}No issues information will be saved as the report file '{}' can't be read.", + lazyArg(this::logPrefix), report.getPath(), e); + } + } + + /** + * Returns a java.io.File for the given path. + * If path is not absolute, returns a File with module base directory as parent path. + */ + static File getIOFile(File baseDir, String path) { + File file = new File(path); + if (!file.isAbsolute()) { + file = new File(baseDir, path); + } + return file; + } + + @CheckForNull + InputFile getInputFile(SensorContext context, String filePath) { + FilePredicates predicates = context.fileSystem().predicates(); + InputFile inputFile = context.fileSystem().inputFile(predicates.or(predicates.hasRelativePath(filePath), predicates.hasAbsolutePath(filePath))); + if (inputFile == null) { + LOG.warn("{}No input file found for {}. No {} issues will be imported on this file.", lazyArg(this::logPrefix), filePath, propertyName()); + } + return inputFile; + } + + void addLineIssue(SensorContext context, ExternalIssue issue) { + InputFile inputFile = getInputFile(context, issue.filename); + if (inputFile != null) { + NewExternalIssue newExternalIssue = context.newExternalIssue(); + NewIssueLocation primaryLocation = newExternalIssue.newLocation() + .message(issue.message) + .on(inputFile) + .at(inputFile.selectLine(issue.lineNumber)); + + newExternalIssue + .at(primaryLocation) + .ruleId(issue.ruleKey) + .engineId(issue.linter) + .type(issue.type) + .severity(DEFAULT_SEVERITY) + .remediationEffortMinutes(DEFAULT_REMEDIATION_COST) + .save(); + } + } + + public static void createExternalRuleRepository(Context context, String linterId, String linterName) { + NewRepository externalRepo = context.createExternalRepository(linterId, GoLanguage.KEY).setName(linterName); + String pathToRulesMeta = "org/sonar/l10n/go/rules/" + linterId + "/rules.json"; + + try (InputStreamReader inputStreamReader = new InputStreamReader(AbstractReportSensor.class.getClassLoader().getResourceAsStream(pathToRulesMeta), StandardCharsets.UTF_8)) { + JsonArray jsonArray = Json.parse(inputStreamReader).asArray(); + for(JsonValue jsonValue : jsonArray) { + JsonObject rule = jsonValue.asObject(); + NewRule newRule = externalRepo.createRule(rule.getString("key", null)) + .setName(rule.getString("name", null)) + .setHtmlDescription(rule.getString("description", null)); + newRule.setDebtRemediationFunction(newRule.debtRemediationFunctions().constantPerIssue(DEFAULT_REMEDIATION_COST + "min")); + if (linterId.equals(GoVetReportSensor.LINTER_ID)) { + newRule.setType(RuleType.BUG); + } + } + } catch (IOException e) { + throw new IllegalStateException("Can't read resource: " + pathToRulesMeta, e); + } + + externalRepo.done(); + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/ExternalIssue.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/ExternalIssue.java new file mode 100644 index 00000000..d589b570 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/ExternalIssue.java @@ -0,0 +1,53 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import javax.annotation.Nullable; +import org.sonar.api.rules.RuleType; + +import static org.sonar.go.externalreport.AbstractReportSensor.GENERIC_ISSUE_KEY; + +class ExternalIssue { + + final String linter; + final RuleType type; + final String ruleKey; + final String filename; + final int lineNumber; + final String message; + + ExternalIssue(String linter, RuleType type, @Nullable String ruleKey, String filename, int lineNumber, String message) { + this.linter = linter; + this.type = type; + this.ruleKey = mapRuleKey(linter, ruleKey, message); + this.filename = filename; + this.lineNumber = lineNumber; + this.message = message; + } + + private static String mapRuleKey(String linter, @Nullable String ruleKey, String message) { + if (ruleKey != null) { + return ruleKey; + } + String key = ExternalKeyUtils.lookup(message, linter); + return key != null ? key : GENERIC_ISSUE_KEY; + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/ExternalKeyUtils.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/ExternalKeyUtils.java new file mode 100644 index 00000000..f1ce7ac6 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/ExternalKeyUtils.java @@ -0,0 +1,121 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; + +public class ExternalKeyUtils { + + private ExternalKeyUtils() { + // utility class, forbidden constructor + } + + public static final List GO_VET_KEYS = Collections.unmodifiableList(Arrays.asList( + new ExternalKey("asmdecl", msg -> msg.contains("(FP)") || msg.contains("wrong argument size") || msg.endsWith("points beyond argument frame")), + new ExternalKey("assign", msg -> msg.startsWith("self-assignment of")), + new ExternalKey("atomic", msg -> msg.equals("direct assignment to atomic value")), + new ExternalKey("bool", msg -> msg.startsWith("redundant") || msg.startsWith("suspect")), + new ExternalKey("buildtags", msg -> msg.contains("build comment")), + new ExternalKey("cgocall", msg -> msg.equals("possibly passing Go type with embedded pointer to C")), + new ExternalKey("composites", msg -> msg.endsWith("composite literal uses unkeyed fields")), + new ExternalKey("copylocks", msg -> msg.contains("passes lock by value:") || msg.contains("copies lock")), + new ExternalKey("httpresponse", msg -> msg.endsWith("before checking for errors")), + new ExternalKey("lostcancel", msg -> msg.matches("the cancel\\d? function .*") || msg.contains("without using the cancel")), + new ExternalKey("methods", msg -> msg.contains("should have signature")), + new ExternalKey("nilfunc", msg -> msg.contains("comparison of function")), + new ExternalKey("printf", msg -> msg.matches("(Printf|Println|Sprintf|Sprintln|Logf|Log) .*") || msg.contains("formatting directive")), + new ExternalKey("rangeloops", msg -> msg.startsWith("loop variable")), + new ExternalKey("shadow", msg -> msg.contains("shadows declaration at")), + new ExternalKey("shift", msg -> msg.contains("too small for shift")), + new ExternalKey("structtags", msg -> msg.contains("struct field") && msg.contains("tag")), + new ExternalKey("tests", msg -> + msg.contains("has malformed") || + msg.contains("refers to unknown") || + msg.endsWith("should return nothing") || + msg.endsWith("should be niladic")), + new ExternalKey("unreachable", msg -> msg.equals("unreachable code")), + new ExternalKey("unusedresult", msg -> msg.endsWith("call not used")), + new ExternalKey("unsafeptr", msg -> msg.equals("possible misuse of unsafe.Pointer")) + )); + + + public static final List GO_LINT_KEYS = Collections.unmodifiableList(Arrays.asList( + new ExternalKey("PackageComment", msg -> + msg.startsWith("package comment should be of the form") || + msg.startsWith("package comment should not have leading space") || + msg.equals("package comment is detached; there should be no blank lines between it and the package statement") || + msg.equals("should have a package comment, unless it's in another file for this package")), + new ExternalKey("BlankImports", msg -> msg.equals("a blank import should be only in a main or test package, or have a comment justifying it")), + new ExternalKey("Imports", msg -> msg.equals("should not use dot imports")), + new ExternalKey("Exported", msg -> (msg.startsWith("exported") && msg.endsWith("or be unexported")) || + msg.startsWith("comment on exported") || + msg.endsWith("should have its own declaration") || + msg.contains("by other packages, and that stutters; consider calling this")), + new ExternalKey("VarDecls", msg -> msg.contains("from declaration of var")), + new ExternalKey("Elses", msg -> msg.startsWith("if block ends with a return statement, so drop this else and outdent its block")), + new ExternalKey("Ranges", msg -> msg.contains("from range; this loop is equivalent to")), + new ExternalKey("Errorf", msg -> msg.contains("(fmt.Sprintf(...)) with") && msg.contains(".Errorf(...)")), + new ExternalKey("Errors", msg -> msg.startsWith("error var ") && msg.contains("should have name of the form ")), + new ExternalKey("ErrorStrings", msg -> msg.equals("error strings should not be capitalized or end with punctuation or a newline")), + new ExternalKey("ReceiverNames", msg -> + msg.contains("should be consistent with previous receiver name") || + msg.startsWith("receiver name should not be an underscore") || + msg.equals("receiver name should be a reflection of its identity; don't use generic names such as \"this\" or \"self\"")), + new ExternalKey("IncDec", msg -> msg.startsWith("should replace") && !msg.contains("(fmt.Sprintf(...)) with")), + new ExternalKey("ErrorReturn", msg -> msg.startsWith("error should be the last type when returning multiple items")), + new ExternalKey("UnexportedReturn", msg -> msg.contains("returns unexported type") && msg.endsWith("which can be annoying to use")), + new ExternalKey("TimeNames", msg -> msg.contains("don't use unit-specific suffix")), + new ExternalKey("ContextKeyTypes", msg -> msg.startsWith("should not use basic type") && msg.endsWith("as key in context.WithValue")), + new ExternalKey("ContextArgs", msg -> msg.equals("context.Context should be the first parameter of a function")), + new ExternalKey("Names", msg -> + msg.startsWith("don't use an underscore in package name") || + msg.startsWith("don't use ALL_CAPS in Go names; use CamelCase") || + msg.startsWith("don't use leading k in Go names;") || + msg.startsWith("don't use underscores in Go names;") || + msg.matches("(range var|struct field|[\\w]+) [\\w_]+ should be [\\w_]+") || + msg.startsWith("don't use MixedCaps in package name;") + ) + )); + + public static String lookup(String message, String linter) { + if (linter.equals(GoVetReportSensor.LINTER_ID) || linter.equals(GoLintReportSensor.LINTER_ID)) { + List keys = linter.equals(GoVetReportSensor.LINTER_ID) ? GO_VET_KEYS : GO_LINT_KEYS; + return keys.stream() + .filter(externalKey -> externalKey.matches.test(message)) + .map(externalKey -> externalKey.key) + .findFirst() + .orElse(null); + } + return null; + } + + public static class ExternalKey { + public final String key; + public final Predicate matches; + + ExternalKey(String key, Predicate matches) { + this.key = key; + this.matches = matches; + } + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoLintReportSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoLintReportSensor.java new file mode 100644 index 00000000..43ec624b --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoLintReportSensor.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.rules.RuleType; + +import static org.sonarsource.slang.utils.LogArg.lazyArg; + +public class GoLintReportSensor extends AbstractReportSensor { + + private static final Logger LOG = LoggerFactory.getLogger(GoLintReportSensor.class); + + public static final String PROPERTY_KEY = "sonar.go.golint.reportPaths"; + + private static final Pattern GO_LINT_LINE_REGEX = Pattern.compile("(?[^:]+):(?\\d+):\\d*:(?.*)"); + + public static final String LINTER_ID = "golint"; + public static final String LINTER_NAME = "Golint"; + + public GoLintReportSensor(AnalysisWarnings analysisWarnings) { + super(analysisWarnings, LINTER_ID, LINTER_NAME, PROPERTY_KEY); + } + + @Nullable + @Override + ExternalIssue parse(String line) { + Matcher matcher = GO_LINT_LINE_REGEX.matcher(line); + if (matcher.matches()) { + String filename = matcher.group("file").trim(); + int lineNumber = Integer.parseInt(matcher.group("line").trim()); + String message = matcher.group("message").trim(); + return new ExternalIssue(LINTER_ID, RuleType.CODE_SMELL, null, filename, lineNumber, message); + } else { + LOG.debug("{}Unexpected line: {}", lazyArg(this::logPrefix), line); + } + return null; + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoMetaLinterReportSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoMetaLinterReportSensor.java new file mode 100644 index 00000000..f4ae85a3 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoMetaLinterReportSensor.java @@ -0,0 +1,77 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.rules.RuleType; + +import static org.sonarsource.slang.utils.LogArg.lazyArg; + +public class GoMetaLinterReportSensor extends AbstractReportSensor { + + private static final Logger LOG = LoggerFactory.getLogger(GoMetaLinterReportSensor.class); + + public static final String PROPERTY_KEY = "sonar.go.gometalinter.reportPaths"; + + private static final Pattern GO_META_LINTER_REGEX = Pattern.compile("(?[^:]+):(?\\d+):\\d*:" + + "(?(error|warning)):(?.*)\\((?[^\\(]*)\\)"); + + private static final Pattern RULE_KEY_REGEX = Pattern.compile("\\((?[A-Za-z0-9_-]{1,20})\\)$"); + + public GoMetaLinterReportSensor(AnalysisWarnings analysisWarnings) { + super(analysisWarnings, "gometalinter", "GoMetaLinter", PROPERTY_KEY); + } + + @Nullable + @Override + ExternalIssue parse(String line) { + Matcher matcher = GO_META_LINTER_REGEX.matcher(line); + if (matcher.matches()) { + String linter = mapLinterName(matcher.group("linter").trim()); + RuleType type = "error".equals(matcher.group("severity")) ? RuleType.BUG : RuleType.CODE_SMELL; + String filename = matcher.group("file").trim(); + int lineNumber = Integer.parseInt(matcher.group("line").trim()); + String message = matcher.group("message").trim(); + Matcher ruleKeyMatcher = RULE_KEY_REGEX.matcher(message); + String ruleKey = null; + if (ruleKeyMatcher.find()) { + ruleKey = ruleKeyMatcher.group("ruleKey"); + message = message.substring(0, ruleKeyMatcher.start()).trim(); + } + return new ExternalIssue(linter, type, ruleKey, filename, lineNumber, message); + } else { + LOG.debug("{}Unexpected line: {}", lazyArg(this::logPrefix), line); + } + return null; + } + + private static String mapLinterName(String linter) { + if ("vet".equals(linter)) { + return GoVetReportSensor.LINTER_ID; + } + return linter; + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoVetReportSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoVetReportSensor.java new file mode 100644 index 00000000..dcc14d5b --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GoVetReportSensor.java @@ -0,0 +1,62 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.rules.RuleType; + +import static org.sonarsource.slang.utils.LogArg.lazyArg; + +public class GoVetReportSensor extends AbstractReportSensor { + + private static final Logger LOG = LoggerFactory.getLogger(GoVetReportSensor.class); + + public static final String PROPERTY_KEY = "sonar.go.govet.reportPaths"; + + private static final Pattern GO_VET_LINE_REGEX = Pattern.compile("(?[^:]+):(?\\d+):(?.*)"); + + public static final String LINTER_ID = "govet"; + public static final String LINTER_NAME = "go vet"; + + public GoVetReportSensor(AnalysisWarnings analysisWarnings) { + super(analysisWarnings, LINTER_ID, LINTER_NAME, PROPERTY_KEY); + } + + @Nullable + @Override + ExternalIssue parse(String line) { + Matcher matcher = GO_VET_LINE_REGEX.matcher(line); + if (matcher.matches()) { + String filename = matcher.group("file").trim(); + int lineNumber = Integer.parseInt(matcher.group("line").trim()); + String message = matcher.group("message").trim(); + return new ExternalIssue(LINTER_ID, RuleType.BUG, null, filename, lineNumber, message); + } else if (!line.startsWith("exit status")) { + LOG.debug("{}Unexpected line: {}", lazyArg(this::logPrefix), line); + } + return null; + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GolangCILintReportSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GolangCILintReportSensor.java new file mode 100644 index 00000000..58882894 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/GolangCILintReportSensor.java @@ -0,0 +1,86 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.io.File; +import java.util.Locale; +import java.util.function.Consumer; +import javax.annotation.Nullable; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rules.RuleType; +import org.sonar.go.plugin.GoLanguage; +import org.sonarsource.slang.externalreport.CheckstyleFormatImporter; +import org.sonarsource.slang.plugin.AbstractPropertyHandlerSensor; + +public class GolangCILintReportSensor extends AbstractPropertyHandlerSensor { + + public static final String LINTER_KEY = "golangci-lint"; + public static final String LINTER_NAME = "GolangCI-Lint"; + + public static final String PROPERTY_KEY = "sonar.go.golangci-lint.reportPaths"; + + public GolangCILintReportSensor(AnalysisWarnings analysisWarnings) { + super(analysisWarnings, LINTER_KEY, LINTER_NAME, PROPERTY_KEY, GoLanguage.KEY); + } + + @Override + public Consumer reportConsumer(SensorContext context) { + return new GolangCILintCheckstyleFormatImporter(context, LINTER_KEY)::importFile; + } + + private static class GolangCILintCheckstyleFormatImporter extends CheckstyleFormatImporter { + + public GolangCILintCheckstyleFormatImporter(SensorContext context, String linterKey) { + super(context, linterKey); + } + + /** + * Current strategy to define rule type for Golangci-lint: + *
    + *
  • (null, "gosec") -> VULNERABILITY + *
  • ("error", null) -> BUG + *
  • (not "error", null) -> CODE_SMELL + *
+ * + * Gosec is the only linter importing VULNERABILITY issues for now. + */ + @Override + protected RuleType ruleType(@Nullable String severity, String source) { + if ("gosec".equals(source)) { + return RuleType.VULNERABILITY; + } + return super.ruleType(severity, source); + } + + @Override + protected RuleKey createRuleKey(String source, RuleType ruleType, Severity ruleSeverity) { + if ("gosec".equals(source)) { + // gosec issues are exclusively "major vulnerability", keeping "gosec" as rule key. + return RuleKey.of(linterKey, source); + } + String ruleKey = String.format("%s.%s.%s", source, ruleType.toString().toLowerCase(Locale.ROOT), + ruleSeverity.toString().toLowerCase(Locale.ROOT)); + return RuleKey.of(linterKey, ruleKey); + } + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/package-info.java b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/package-info.java new file mode 100644 index 00000000..49895dd7 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/externalreport/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube Go Plugin + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.go.externalreport; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoCheckList.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoCheckList.java new file mode 100644 index 00000000..7665ef38 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoCheckList.java @@ -0,0 +1,74 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.sonar.go.checks.DuplicateBranchGoCheck; +import org.sonarsource.slang.checks.BadClassNameCheck; +import org.sonarsource.slang.checks.CheckList; +import org.sonarsource.slang.checks.CodeAfterJumpCheck; +import org.sonarsource.slang.checks.CollapsibleIfStatementsCheck; +import org.sonarsource.slang.checks.DuplicateBranchCheck; +import org.sonarsource.slang.checks.OneStatementPerLineCheck; +import org.sonarsource.slang.checks.TabsCheck; +import org.sonarsource.slang.checks.UnusedFunctionParameterCheck; +import org.sonarsource.slang.checks.UnusedLocalVariableCheck; +import org.sonarsource.slang.checks.UnusedPrivateMethodCheck; + +import org.sonar.go.checks.CodeAfterJumpGoCheck; +import org.sonar.go.checks.OneStatementPerLineGoCheck; + +public class GoCheckList { + + private GoCheckList() { + // utility class + } + + static final Class[] GO_CHECK_BLACK_LIST = { + BadClassNameCheck.class, + // Can not enable rule S1066, as Go if-trees are containing an initializer, not well handled by SLang + CollapsibleIfStatementsCheck.class, + TabsCheck.class, + // Can not enable rule S1172 since it it not possible to identify overridden function with modifier (to avoid FP) + UnusedFunctionParameterCheck.class, + UnusedLocalVariableCheck.class, + UnusedPrivateMethodCheck.class, + // Replaced by language specific test + CodeAfterJumpCheck.class, + DuplicateBranchCheck.class, + OneStatementPerLineCheck.class + }; + + private static final Collection> GO_LANGUAGE_SPECIFIC_CHECKS = Arrays.asList( + CodeAfterJumpGoCheck.class, + DuplicateBranchGoCheck.class, + OneStatementPerLineGoCheck.class + ); + + public static List> checks() { + List> list = new ArrayList<>(CheckList.excludeChecks(GO_CHECK_BLACK_LIST)); + list.addAll(GO_LANGUAGE_SPECIFIC_CHECKS); + return list; + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoExclusionsFileFilter.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoExclusionsFileFilter.java new file mode 100644 index 00000000..564d3186 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoExclusionsFileFilter.java @@ -0,0 +1,45 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputFileFilter; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.WildcardPattern; + +public class GoExclusionsFileFilter implements InputFileFilter { + + private final Configuration configuration; + + public GoExclusionsFileFilter(Configuration configuration) { + this.configuration = configuration; + } + + @Override + public boolean accept(InputFile inputFile) { + if (!GoLanguage.KEY.equals(inputFile.language())) { + return true; + } + String[] excludedPatterns = this.configuration.getStringArray(GoPlugin.EXCLUSIONS_KEY); + String relativePath = inputFile.uri().toString(); + return !WildcardPattern.match(WildcardPattern.create(excludedPatterns), relativePath); + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoLanguage.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoLanguage.java new file mode 100644 index 00000000..584486ef --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoLanguage.java @@ -0,0 +1,46 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.sonar.api.config.Configuration; +import org.sonar.api.resources.AbstractLanguage; + +public class GoLanguage extends AbstractLanguage { + + public static final String KEY = "go"; + public static final String FILE_SUFFIXES_KEY = "sonar.go.file.suffixes"; + public static final String FILE_SUFFIXES_DEFAULT_VALUE = ".go"; + + private final Configuration configuration; + + public GoLanguage(Configuration configuration) { + super(KEY, "Go"); + this.configuration = configuration; + } + + @Override + public String[] getFileSuffixes() { + String[] suffixes = configuration.getStringArray(FILE_SUFFIXES_KEY); + if (suffixes.length == 0) { + suffixes = new String[] {FILE_SUFFIXES_DEFAULT_VALUE}; + } + return suffixes; + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoPlugin.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoPlugin.java new file mode 100644 index 00000000..f1429cf9 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoPlugin.java @@ -0,0 +1,157 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.sonar.api.Plugin; +import org.sonar.api.SonarProduct; +import org.sonar.api.config.PropertyDefinition; +import org.sonar.api.resources.Qualifiers; +import org.sonar.go.coverage.GoCoverSensor; +import org.sonar.go.externalreport.GoLintReportSensor; +import org.sonar.go.externalreport.GoMetaLinterReportSensor; +import org.sonar.go.externalreport.GoVetReportSensor; +import org.sonar.go.externalreport.GolangCILintReportSensor; +import org.sonar.go.testreport.GoTestSensor; + +public class GoPlugin implements Plugin { + + static final String RESOURCE_FOLDER = "org/sonar/l10n/go/rules/go"; + + public static final String EXCLUSIONS_KEY = "sonar.go.exclusions"; + public static final String EXCLUSIONS_DEFAULT_VALUE = "**/vendor/**"; + + private static final String GO_CATEGORY = "Go"; + private static final String TEST_COVERAGE_SUBCATEGORY = "Test and Coverage"; + private static final String EXTERNAL_LINTER_SUBCATEGORY = "Popular Rule Engines"; + private static final String GENERAL_SUBCATEGORY = "General"; + + @Override + public void define(Context context) { + + context.addExtensions( + GoLanguage.class, + InstanceScopeGoConverter.class, + GoSensor.class, + GoExclusionsFileFilter.class, + GoRulesDefinition.class, + GoProfileDefinition.class, + + PropertyDefinition.builder(GoLanguage.FILE_SUFFIXES_KEY) + .index(10) + .name("File Suffixes") + .description("List of suffixes for files to analyze.") + .category(GO_CATEGORY) + .subCategory(GENERAL_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .defaultValue(GoLanguage.FILE_SUFFIXES_DEFAULT_VALUE) + .multiValues(true) + .build(), + + PropertyDefinition.builder(EXCLUSIONS_KEY) + .index(11) + .defaultValue(EXCLUSIONS_DEFAULT_VALUE) + .name("Go Exclusions") + .description("List of file path patterns to be excluded from analysis of Go files.") + .category(GO_CATEGORY) + .subCategory(GENERAL_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .multiValues(true) + .build() + ); + + if (context.getRuntime().getProduct() != SonarProduct.SONARLINT) { + addReportExtensions(context); + } + } + + private static void addReportExtensions(Context context) { + context.addExtensions( + GoTestSensor.class, + GoCoverSensor.class, + GoVetReportSensor.class, + GoLintReportSensor.class, + GoMetaLinterReportSensor.class, + GolangCILintReportSensor.class, + PropertyDefinition.builder(GoTestSensor.REPORT_PATH_KEY) + .index(19) + .name("Path to test execution report(s)") + .description("Path to test execution reports generated by Go with '-json' key, available since go1.10 (e.g.: go test -json > test-report.out).") + .category(GO_CATEGORY) + .subCategory(TEST_COVERAGE_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .multiValues(true) + .build(), + + PropertyDefinition.builder(GoCoverSensor.REPORT_PATH_KEY) + .index(20) + .name("Path to coverage report(s)") + .description("Path to coverage reports generated by Go (e.g.: go test -coverprofile=coverage.out), ant patterns relative to project root are supported.") + .category(GO_CATEGORY) + .subCategory(TEST_COVERAGE_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .multiValues(true) + .build(), + + PropertyDefinition.builder(GoVetReportSensor.PROPERTY_KEY) + .index(30) + .name("\"go vet\" Report Files") + .description("Paths (absolute or relative) to the files with \"go vet\" issues.") + .category(GO_CATEGORY) + .subCategory(EXTERNAL_LINTER_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .defaultValue("") + .multiValues(true) + .build(), + + PropertyDefinition.builder(GoLintReportSensor.PROPERTY_KEY) + .index(31) + .name("Golint Report Files") + .description("Paths (absolute or relative) to the files with Golint issues.") + .category(GO_CATEGORY) + .subCategory(EXTERNAL_LINTER_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .defaultValue("") + .multiValues(true) + .build(), + + PropertyDefinition.builder(GoMetaLinterReportSensor.PROPERTY_KEY) + .index(32) + .name("GoMetaLinter Report Files") + .description("Paths (absolute or relative) to the files with GoMetaLinter issues.") + .category(GO_CATEGORY) + .subCategory(EXTERNAL_LINTER_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .defaultValue("") + .multiValues(true) + .build(), + + PropertyDefinition.builder(GolangCILintReportSensor.PROPERTY_KEY) + .index(33) + .name("GolangCI-Lint Report Files") + .description("Paths (absolute or relative) to the files with GolangCI-Lint issues.") + .category(GO_CATEGORY) + .subCategory(EXTERNAL_LINTER_SUBCATEGORY) + .onQualifiers(Qualifiers.PROJECT) + .defaultValue("") + .multiValues(true) + .build() + ); + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoProfileDefinition.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoProfileDefinition.java new file mode 100644 index 00000000..de895ebf --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoProfileDefinition.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonarsource.analyzer.commons.BuiltInQualityProfileJsonLoader; + +public class GoProfileDefinition implements BuiltInQualityProfilesDefinition { + + static final String PATH_TO_JSON = "org/sonar/l10n/go/rules/go/Sonar_way_profile.json"; + + @Override + public void define(Context context) { + NewBuiltInQualityProfile profile = context.createBuiltInQualityProfile("Sonar way", GoLanguage.KEY); + BuiltInQualityProfileJsonLoader.load(profile, GoRulesDefinition.REPOSITORY_KEY, PATH_TO_JSON); + profile.setDefault(true); + profile.done(); + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoRulesDefinition.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoRulesDefinition.java new file mode 100644 index 00000000..2d86f739 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoRulesDefinition.java @@ -0,0 +1,59 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import java.util.List; + +import org.sonar.api.SonarRuntime; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.go.externalreport.AbstractReportSensor; +import org.sonar.go.externalreport.GoLintReportSensor; +import org.sonar.go.externalreport.GoVetReportSensor; +import org.sonarsource.analyzer.commons.RuleMetadataLoader; +import org.sonarsource.slang.checks.utils.Language; +import org.sonarsource.slang.plugin.RulesDefinitionUtils; + +public class GoRulesDefinition implements RulesDefinition { + + public static final String REPOSITORY_KEY = "go"; + + private final SonarRuntime runtime; + + public GoRulesDefinition(SonarRuntime runtime) { + this.runtime = runtime; + } + + @Override + public void define(Context context) { + NewRepository repository = context.createRepository(REPOSITORY_KEY, GoLanguage.KEY) + .setName("SonarAnalyzer"); + RuleMetadataLoader metadataLoader = new RuleMetadataLoader(GoPlugin.RESOURCE_FOLDER, GoProfileDefinition.PATH_TO_JSON, runtime); + + List> checks = GoCheckList.checks(); + metadataLoader.addRulesByAnnotatedClass(repository, checks); + + RulesDefinitionUtils.setDefaultValuesForParameters(repository, checks, Language.GO); + + repository.done(); + + AbstractReportSensor.createExternalRuleRepository(context, GoVetReportSensor.LINTER_ID, GoVetReportSensor.LINTER_NAME); + AbstractReportSensor.createExternalRuleRepository(context, GoLintReportSensor.LINTER_ID, GoLintReportSensor.LINTER_NAME); + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoSensor.java new file mode 100644 index 00000000..203b9ba2 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/GoSensor.java @@ -0,0 +1,86 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import java.util.function.Predicate; +import org.sonar.api.SonarRuntime; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.rule.Checks; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.issue.NoSonarFilter; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonar.go.converter.GoConverter; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.VariableDeclarationTree; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.plugin.SlangSensor; + +public class GoSensor extends SlangSensor { + + private final Checks checks; + + private ASTConverter goConverter = null; + + public GoSensor(SonarRuntime sonarRuntime, CheckFactory checkFactory, FileLinesContextFactory fileLinesContextFactory, + NoSonarFilter noSonarFilter, GoLanguage language, GoConverter goConverter) { + super(sonarRuntime, noSonarFilter, fileLinesContextFactory, language); + checks = checkFactory.create(GoRulesDefinition.REPOSITORY_KEY); + checks.addAnnotatedChecks((Iterable) GoCheckList.checks()); + this.goConverter = goConverter; + } + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor.onlyOnLanguage(GoLanguage.KEY) + .name("Code Quality and Security for Go"); + processesFilesIndependently(descriptor); + } + + @Override + protected ASTConverter astConverter(SensorContext sensorContext) { + return goConverter; + } + + @Override + protected Checks checks() { + return checks; + } + + @Override + protected String repositoryKey() { + return GoRulesDefinition.REPOSITORY_KEY; + } + + @Override + protected Predicate executableLineOfCodePredicate() { + return super.executableLineOfCodePredicate().and(t -> + !(t instanceof VariableDeclarationTree) + && !isGenericDeclaration(t) + ); + } + + private static boolean isGenericDeclaration(Tree tree) { + return tree instanceof NativeTree && + ((NativeTree) tree).nativeKind().toString().contains("GenDecl"); + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/InstanceScopeGoConverter.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/InstanceScopeGoConverter.java new file mode 100644 index 00000000..b000356b --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/InstanceScopeGoConverter.java @@ -0,0 +1,35 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.sonar.api.scanner.ScannerSide; +import org.sonar.api.utils.TempFolder; +import org.sonar.go.converter.GoConverter; +import org.sonarsource.api.sonarlint.SonarLintSide; + +@ScannerSide +@SonarLintSide(lifespan = SonarLintSide.INSTANCE) +public class InstanceScopeGoConverter extends GoConverter { + + public InstanceScopeGoConverter(TempFolder tempFolder) { + super(tempFolder.newDir()); + } + +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/plugin/package-info.java b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/package-info.java new file mode 100644 index 00000000..29aeaebf --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/plugin/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube Go Plugin + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.go.plugin; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/testreport/GoTestSensor.java b/sonar-go-plugin/src/main/java/org/sonar/go/testreport/GoTestSensor.java new file mode 100644 index 00000000..559f4d90 --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/testreport/GoTestSensor.java @@ -0,0 +1,247 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.testreport; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonObject; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.annotation.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.api.batch.fs.FilePredicate; +import org.sonar.api.batch.fs.FilePredicates; +import org.sonar.api.batch.fs.FileSystem; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.batch.sensor.SensorDescriptor; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.go.coverage.GoPathContext; +import org.sonar.go.plugin.GoLanguage; + +public class GoTestSensor implements Sensor { + + private static final Logger LOG = LoggerFactory.getLogger(GoTestSensor.class); + + public static final String REPORT_PATH_KEY = "sonar.go.tests.reportPaths"; + + GoPathContext goPathContext = GoPathContext.DEFAULT; + + // caching package <-> test input files + private Map> testFilesByPackage = new HashMap<>(); + + @Override + public void describe(SensorDescriptor descriptor) { + descriptor.onlyOnLanguage(GoLanguage.KEY) + .onlyWhenConfiguration(conf -> conf.hasKey(REPORT_PATH_KEY)) + .name("Go Unit Test Report"); + } + + @Override + public void execute(SensorContext context) { + Map> testInfoByFile = new HashMap<>(); + + getReportPaths(context).forEach(path -> parseReport(context, path, testInfoByFile)); + testInfoByFile.forEach((key, value) -> saveTestMetrics(context, key, value)); + } + + private static List getReportPaths(SensorContext context) { + List result = new ArrayList<>(); + String[] reportPaths = context.config().getStringArray(REPORT_PATH_KEY); + for (String reportPath : reportPaths) { + Path path = Paths.get(reportPath); + if (!path.isAbsolute()) { + path = context.fileSystem().baseDir().toPath().resolve(path); + } + if (path.toFile().exists()) { + result.add(path); + } else { + LOG.error("Test report can't be loaded, file not found: '{}', ignoring this file.", path); + } + } + + return result; + } + + private void parseReport(SensorContext context, Path reportPath, Map> testInfoByFile) { + try { + List testInfoList = Files.readAllLines(reportPath).stream() + .filter(line -> line.startsWith("{")) + .map(line -> getRelevantTestInfo(line, reportPath)) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + for (TestInfo testInfo : testInfoList) { + InputFile testFile = findTestFile(context.fileSystem(), testInfo); + + if (testFile != null) { + testInfoByFile + .computeIfAbsent(testFile, key -> new ArrayList<>()) + .add(testInfo); + } else { + LOG.warn("Failed to find test file for package {} and test {}", testInfo.pkg, testInfo.test); + } + } + } catch (IOException e) { + LOG.error("Failed to read unit test report file " + reportPath.toString(), e); + } + } + + @Nullable + private static TestInfo getRelevantTestInfo(String line, Path reportPath) { + try { + TestInfo testInfo = new TestInfo(Json.parse(line).asObject()); + if (testInfo.isRelevant()) { + return testInfo; + } + } catch (Exception e) { + LOG.error("Failed to parse unit test report line (file {}):\n {}", reportPath, line); + } + + return null; + } + + @Nullable + InputFile findTestFile(FileSystem fileSystem, TestInfo testInfo) throws IOException { + List testInputFilesInPackage = testFilesByPackage.computeIfAbsent( + testInfo.pkg, + goPackage -> getTestFilesForPackage(fileSystem, goPackage)); + + // If the test was actually a sub-test, the name is of the form + // "TestFunc/Sub_Test_Name". + String testName = testInfo.test.split("/", 2)[0]; + + Pattern pattern = Pattern.compile("^func\\s+" + testName + "\\s*\\(", Pattern.MULTILINE); + for (InputFile testFile : testInputFilesInPackage) { + if (pattern.matcher(testFile.contents()).find()) { + return testFile; + } + } + + return null; + } + + private List getTestFilesForPackage(FileSystem fileSystem, String goPackage) { + FilePredicates predicates = fileSystem.predicates(); + String packageDirectory = goPathContext.resolve(goPackage); + + if (!new File(packageDirectory).exists()) { + packageDirectory = findPackageDirectory(goPackage, fileSystem); + if (packageDirectory == null) { + return Collections.emptyList(); + } + } + + try (Stream stream = Files.list(Paths.get(packageDirectory))) { + return stream + .map(path -> fileSystem.inputFile(testFilePredicate(predicates, path))) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + } catch (IOException e) { + LOG.warn("Failed to read package directory " + packageDirectory, e); + return Collections.emptyList(); + } + } + + private static FilePredicate testFilePredicate(FilePredicates predicates, Path path) { + return predicates.and( + predicates.hasType(Type.TEST), + predicates.hasAbsolutePath(path.toString()), + predicates.hasLanguage(GoLanguage.KEY)); + } + + private static String findPackageDirectory(String packagePath, FileSystem fileSystem) { + File resolved = fileSystem.baseDir().toPath().resolve(packagePath).toFile(); + if (resolved.exists()) { + return resolved.toString(); + } + + Path path = Paths.get(packagePath); + if (path.getNameCount() == 1) { + // It either means that the package last element was the baseDir, or that the test is in the root dir of a go project + // with module (the "Package" will be the name of the module, we should ignore it). + return fileSystem.baseDir().toString(); + } else { + Path subpath = path.subpath(1, path.getNameCount()); + return findPackageDirectory(subpath.toString(), fileSystem); + } + } + + + private static void saveTestMetrics(SensorContext context, InputFile testFile, List tests) { + int skip = 0; + long timeMs = 0; + int fail = 0; + for (TestInfo test : tests) { + timeMs += test.elapsed * 1000; + if (test.action.equals("skip")) { + skip++; + } else if (test.action.equals("fail")) { + fail++; + } + } + + context.newMeasure().on(testFile).withValue(skip).forMetric(CoreMetrics.SKIPPED_TESTS).save(); + context.newMeasure().on(testFile).withValue(timeMs).forMetric(CoreMetrics.TEST_EXECUTION_TIME).save(); + context.newMeasure().on(testFile).withValue(tests.size()).forMetric(CoreMetrics.TESTS).save(); + context.newMeasure().on(testFile).withValue(fail).forMetric(CoreMetrics.TEST_FAILURES).save(); + } + + static class TestInfo { + final String action; + final String pkg; + final String test; + final Double elapsed; + + public TestInfo(@Nullable String action, @Nullable String pkg, @Nullable String test, @Nullable Double elapsed) { + this.action = action; + this.pkg = pkg; + this.test = test; + this.elapsed = elapsed; + } + + TestInfo(JsonObject json) { + this(json.getString("Action", null), + json.getString("Package", null), + json.getString("Test", null), + json.get("Elapsed") != null ? json.getDouble("Elapsed", 0.0d) : null); + } + + boolean isRelevant() { + return action != null && test != null && pkg != null && elapsed != null && + (action.equals("pass") || action.equals("fail") || action.equals("skip")); + } + } +} diff --git a/sonar-go-plugin/src/main/java/org/sonar/go/testreport/package-info.java b/sonar-go-plugin/src/main/java/org/sonar/go/testreport/package-info.java new file mode 100644 index 00000000..2ca4eb1d --- /dev/null +++ b/sonar-go-plugin/src/main/java/org/sonar/go/testreport/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube Go Plugin + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +@ParametersAreNonnullByDefault +package org.sonar.go.testreport; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/ParsingError.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/ParsingError.html new file mode 100644 index 00000000..d7780476 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/ParsingError.html @@ -0,0 +1,4 @@ +

Why is this an issue?

+

When the parser fails, it is possible to record the failure as an issue on the file. This way, not only is it possible to track the number of files +that do not parse but also to easily find out why they do not parse.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/ParsingError.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/ParsingError.json new file mode 100644 index 00000000..b4d18a9e --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/ParsingError.json @@ -0,0 +1,17 @@ +{ + "title": "Go parser failure", + "type": "CODE_SMELL", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "30min" + }, + "tags": [ + "suspicious" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-2260", + "sqKey": "S2260", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S100.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S100.html new file mode 100644 index 00000000..c626e478 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S100.html @@ -0,0 +1,16 @@ +

Why is this an issue?

+

Shared naming conventions allow teams to collaborate efficiently.

+

This rule raises an issue when a function name does not match a provided regular expression.

+

For example, with the default provided regular expression ^(_|[a-zA-Z0-9]+)$, the function:

+
+func execute_all() {
+...
+}
+
+

should be renamed to

+
+func executeAll() {
+...
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S100.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S100.json new file mode 100644 index 00000000..eb3460d3 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S100.json @@ -0,0 +1,23 @@ +{ + "title": "Function names should comply with a naming convention", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "IDENTIFIABLE" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "convention" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-100", + "sqKey": "S100", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S103.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S103.html new file mode 100644 index 00000000..013131f8 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S103.html @@ -0,0 +1,3 @@ +

Why is this an issue?

+

Scrolling horizontally to see a full line of code lowers the code readability.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S103.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S103.json new file mode 100644 index 00000000..d9faeb16 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S103.json @@ -0,0 +1,23 @@ +{ + "title": "Lines should not be too long", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "FORMATTED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "1min" + }, + "tags": [ + "convention" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-103", + "sqKey": "S103", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S104.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S104.html new file mode 100644 index 00000000..ab59359a --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S104.html @@ -0,0 +1,5 @@ +

Why is this an issue?

+

When a source file grows too much, it can accumulate numerous responsibilities and become challenging to understand and maintain.

+

Above a specific threshold, refactor the file into smaller files whose code focuses on well-defined tasks. Those smaller files will be easier to +understand and test.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S104.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S104.json new file mode 100644 index 00000000..0c772033 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S104.json @@ -0,0 +1,23 @@ +{ + "title": "Files should not have too many lines of code", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "1h" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-104", + "sqKey": "S104", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1067.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1067.html new file mode 100644 index 00000000..df157376 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1067.html @@ -0,0 +1,14 @@ +

Why is this an issue?

+

The complexity of an expression is defined by the number of &&, || and condition ? ifTrue : ifFalse +operators it contains.

+

A single expression’s complexity should not become too high to keep the code readable.

+

Noncompliant code example

+

With the default threshold value of 3:

+
+if (((condition1 && condition2) || (condition3 && condition4)) && condition5) { ... }
+
+

Compliant solution

+
+if ( (myFirstCondition() || mySecondCondition()) && myLastCondition()) { ... }
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1067.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1067.json new file mode 100644 index 00000000..41914450 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1067.json @@ -0,0 +1,25 @@ +{ + "title": "Expressions should not be too complex", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Linear with offset", + "linearDesc": "per complexity point above the threshold", + "linearOffset": "5min", + "linearFactor": "1min" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-1067", + "sqKey": "S1067", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S107.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S107.html new file mode 100644 index 00000000..70a9018a --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S107.html @@ -0,0 +1,38 @@ +

Why is this an issue?

+

Functions with a long parameter list are difficult to use, as maintainers must figure out the role of each parameter and keep track of their +position.

+
+func setCoordinates(x1 int, y1 int, z1 int, x2 int, y2 int, z2 int) { // Noncompliant
+    // ...
+}
+
+

The solution can be to:

+
    +
  • Split the function into smaller ones
  • +
+
+// Each function does a part of what the original setCoordinates function was doing, so confusion risks are lower
+func setOrigin(x int, y int, z int) {
+   // ...
+}
+
+func setSize(width int, height int, depth int) {
+   // ...
+}
+
+
    +
  • Find a better data structure for the parameters that group data in a way that makes sense for the specific application domain
  • +
+
+type point struct { // In geometry, Point is a logical structure to group data
+    x int
+    y int
+    z int
+}
+
+func setCoordinates(p1 point, p2 point) {
+    // ...
+}
+
+

This rule raises an issue when a function has more parameters than the provided threshold.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S107.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S107.json new file mode 100644 index 00000000..712a500c --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S107.json @@ -0,0 +1,23 @@ +{ + "title": "Functions should not have too many parameters", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "20min" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-107", + "sqKey": "S107", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S108.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S108.html new file mode 100644 index 00000000..c289e588 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S108.html @@ -0,0 +1,18 @@ +

Why is this an issue?

+

An empty code block is confusing. It will require some effort from maintainers to determine if it is intentional or indicates the implementation is +incomplete.

+
+func compute(a int, b int) {
+	sum := a + b
+	if  sum > 0 { } // Noncompliant; empty on purpose or missing piece of code?
+	fmt.Println("Result:", sum)
+}
+
+

Removing or filling the empty code blocks takes away ambiguity and generally results in a more straightforward and less surprising code.

+

Exceptions

+

The rule ignores:

+
    +
  • code blocks that contain comments.
  • +
  • for without init and post statements with empty blocks
  • +
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S108.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S108.json new file mode 100644 index 00000000..a56dd449 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S108.json @@ -0,0 +1,23 @@ +{ + "title": "Nested blocks of code should not be left empty", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "suspicious" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-108", + "sqKey": "S108", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1110.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1110.html new file mode 100644 index 00000000..c9435cba --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1110.html @@ -0,0 +1,28 @@ +

Why is this an issue?

+

The use of parentheses, even those not required to enforce a desired order of operations, can clarify the intent behind a piece of code. But +redundant pairs of parentheses could be misleading, and should be removed.

+

Noncompliant code example

+
+func foo(a bool, y int) int {
+  x := (y / 2 + 1)   //Compliant even if the parenthesis are ignored by the compiler
+
+  if a && ((x+y > 0)) {  // Noncompliant
+    //...
+  }
+
+  return ((x + 1))  // Noncompliant
+}
+
+

Compliant solution

+
+func foo(a bool, y int) int {
+  x := (y / 2 + 1)
+
+  if a && (x+y > 0) {
+    //...
+  }
+
+  return (x + 1)
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1110.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1110.json new file mode 100644 index 00000000..2b607b50 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1110.json @@ -0,0 +1,23 @@ +{ + "title": "Redundant pairs of parentheses should be removed", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "1min" + }, + "tags": [ + "confusing" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1110", + "sqKey": "S1110", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1125.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1125.html new file mode 100644 index 00000000..9dc09cec --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1125.html @@ -0,0 +1,19 @@ +

Why is this an issue?

+

Redundant Boolean literals should be removed from expressions to improve readability.

+

Noncompliant code example

+
+if boolFunc() || false {
+    // ...
+}
+
+flag := x && true
+
+

Compliant solution

+
+if boolFunc() {
+    // ...
+}
+
+flag := x
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1125.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1125.json new file mode 100644 index 00000000..0ceb7d6b --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1125.json @@ -0,0 +1,23 @@ +{ + "title": "Boolean literals should not be redundant", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "CONVENTIONAL" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "clumsy" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-1125", + "sqKey": "S1125", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1134.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1134.html new file mode 100644 index 00000000..220cb3af --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1134.html @@ -0,0 +1,15 @@ +

Why is this an issue?

+

FIXME tags are commonly used to mark places where a bug is suspected, but which the developer wants to deal with later.

+

Sometimes the developer will not have the time or will simply forget to get back to that tag.

+

This rule is meant to track those tags and to ensure that they do not go unnoticed.

+
+func foo() {
+  // FIXME
+}
+
+

Resources

+

Documentation

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1134.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1134.json new file mode 100644 index 00000000..ed00cce4 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1134.json @@ -0,0 +1,28 @@ +{ + "title": "Track uses of \"FIXME\" tags", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "COMPLETE" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "0min" + }, + "tags": [ + "cwe" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1134", + "sqKey": "S1134", + "scope": "All", + "securityStandards": { + "CWE": [ + 546 + ] + }, + "quickfix": "infeasible" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1135.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1135.html new file mode 100644 index 00000000..c12d0de7 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1135.html @@ -0,0 +1,28 @@ +

Why is this an issue?

+

Developers often use TOOO tags to mark areas in the code where additional work or improvements are needed but are not implemented +immediately. However, these TODO tags sometimes get overlooked or forgotten, leading to incomplete or unfinished code. This code smell +class aims to identify and address such unattended TODO tags to ensure a clean and maintainable codebase. This description will explore +why this is a problem and how it can be fixed to improve the overall code quality.

+

What is the potential impact?

+

Unattended TODO tags in code can have significant implications for the development process and the overall codebase.

+

Incomplete Functionality: When developers leave TODO tags without implementing the corresponding code, it results in incomplete +functionality within the software. This can lead to unexpected behavior or missing features, adversely affecting the end-user experience.

+

Missed Bug Fixes: If developers do not promptly address TODO tags, they might overlook critical bug fixes and security updates. +Delayed bug fixes can result in more severe issues and increase the effort required to resolve them later.

+

Impact on Collaboration: In team-based development environments, unattended TODO tags can hinder collaboration. Other team members +might not be aware of the intended changes, leading to conflicts or redundant efforts in the codebase.

+

Codebase Bloat: Accumulation of unattended TODO tags over time can clutter the codebase and make it difficult to distinguish between +work in progress and completed code. This bloat can make it challenging to maintain an organized and efficient codebase.

+

Addressing this code smell is essential to ensure a maintainable, readable, reliable codebase and promote effective collaboration among +developers.

+

Noncompliant code example

+
+func foo() {
+  // TODO
+}
+
+

Resources

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1135.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1135.json new file mode 100644 index 00000000..1f6d301f --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1135.json @@ -0,0 +1,28 @@ +{ + "title": "Track uses of \"TODO\" tags", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "COMPLETE" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "0min" + }, + "tags": [ + "cwe" + ], + "defaultSeverity": "Info", + "ruleSpecification": "RSPEC-1135", + "sqKey": "S1135", + "scope": "All", + "securityStandards": { + "CWE": [ + 546 + ] + }, + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1145.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1145.html new file mode 100644 index 00000000..4182e747 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1145.html @@ -0,0 +1,32 @@ +

Why is this an issue?

+

if statements with conditions that are always false have the effect of making blocks of code non-functional. if +statements with conditions that are always true are completely redundant, and make the code less readable.

+

There are three possible causes for the presence of such code:

+
    +
  • An if statement was changed during debugging and that debug code has been committed.
  • +
  • Some value was left unset.
  • +
  • Some logic is not doing what the programmer thought it did.
  • +
+

In any of these cases, unconditional if statements should be removed.

+

Noncompliant code example

+
+if true {
+	doSomething()
+}
+
+if false {
+	doSomething()
+}
+
+

Compliant solution

+
+doSomething();
+...
+
+

Resources

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1145.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1145.json new file mode 100644 index 00000000..df01dd45 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1145.json @@ -0,0 +1,24 @@ +{ + "title": "Useless \"if(true) {...}\" and \"if(false){...}\" blocks should be removed", + "type": "BUG", + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "2min" + }, + "tags": [ + "cwe" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1145", + "sqKey": "S1145", + "scope": "All", + "securityStandards": { + "CWE": [ + 489, + 570, + 571 + ] + }, + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1151.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1151.html new file mode 100644 index 00000000..9d94b59b --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1151.html @@ -0,0 +1,42 @@ +

Why is this an issue?

+

The switch statement should be used only to clearly define some new branches in the control flow. As soon as a case +clause contains too many statements this highly decreases the readability of the overall control flow statement. In such case, the content of the +case clause should be extracted into a dedicated method.

+

Noncompliant code example

+

With the default threshold of 5:

+
+func foo(tag int) {
+	switch tag {
+	case 0:
+		methodCall1()
+		methodCall2()
+		methodCall3()
+		methodCall4()
+                methodCall5()
+                methodCall6()
+	case 1:
+		bar()
+	}
+}
+
+

Compliant solution

+
+func foo(tag int) {
+	switch tag {
+	case 0:
+		executeAll()
+	case 1:
+		bar()
+	}
+}
+
+func executeAll() {
+	methodCall1()
+	methodCall2()
+	methodCall3()
+	methodCall4()
+        methodCall5()
+        methodCall6()
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1151.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1151.json new file mode 100644 index 00000000..788929e0 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1151.json @@ -0,0 +1,23 @@ +{ + "title": "\"switch case\" clauses should not have too many lines", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1151", + "sqKey": "S1151", + "scope": "Main", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S117.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S117.html new file mode 100644 index 00000000..1d8ac4ce --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S117.html @@ -0,0 +1,19 @@ +

Why is this an issue?

+

Shared naming conventions allow teams to collaborate effectively. This rule raises an issue when a local variable or function parameter name does +not match the provided regular expression.

+

Noncompliant code example

+

With the default regular expression ^(_|[a-zA-Z0-9]+)$:

+
+func doSomething(my_param int) { // Noncompliant
+  var local_ int; // Noncompliant
+  ...
+}
+
+

Compliant solution

+
+func doSomething(myParam int) {
+  var local int;
+  ...
+}{code}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S117.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S117.json new file mode 100644 index 00000000..07441e13 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S117.json @@ -0,0 +1,23 @@ +{ + "title": "Local variable and function parameter names should comply with a naming convention", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "IDENTIFIABLE" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "2min" + }, + "tags": [ + "convention" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-117", + "sqKey": "S117", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1186.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1186.html new file mode 100644 index 00000000..5c671be4 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1186.html @@ -0,0 +1,19 @@ +

Why is this an issue?

+

There are several reasons for a method not to have a method body:

+
    +
  • It is an unintentional omission, and should be fixed to prevent an unexpected behavior in production.
  • +
  • It is not yet, or never will be, supported. In this case an exception should be thrown.
  • +
  • The method is an intentionally-blank override. In this case a nested comment should explain the reason for the blank override.
  • +
+

Noncompliant code example

+
+func doNothing() { // Noncompliant
+}
+
+

Compliant solution

+
+func doNothing() {
+  // Do nothing because of X and Y.
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1186.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1186.json new file mode 100644 index 00000000..4362ff4b --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1186.json @@ -0,0 +1,23 @@ +{ + "title": "Functions should not be empty", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "COMPLETE" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "suspicious" + ], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-1186", + "sqKey": "S1186", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1192.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1192.html new file mode 100644 index 00000000..eb763038 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1192.html @@ -0,0 +1,26 @@ +

Why is this an issue?

+

Duplicated string literals make the process of refactoring error-prone, since you must be sure to update all occurrences.

+

On the other hand, constants can be referenced from many places, but only need to be updated in a single place.

+

Noncompliant code example

+

With the default threshold of 3:

+
+func run() {
+	prepare("This should be a constant")  // Noncompliant; 'This should ...' is duplicated 3 times
+	execute("This should be a constant")
+	release("This should be a constant")
+}
+
+

Compliant solution

+
+const ACTION = "This should be a constant"
+
+func run() {
+	prepare(ACTION)
+	execute(ACTION)
+	release(ACTION)
+}
+
+

Exceptions

+

To prevent generating some false-positives, literals having 5 or less characters are excluded as well as literals containing only letters, digits +and '_'.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1192.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1192.json new file mode 100644 index 00000000..797dfcc7 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1192.json @@ -0,0 +1,25 @@ +{ + "title": "String literals should not be duplicated", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "DISTINCT" + }, + "status": "ready", + "remediation": { + "func": "Linear with offset", + "linearDesc": "per duplicate instance", + "linearOffset": "2min", + "linearFactor": "2min" + }, + "tags": [ + "design" + ], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-1192", + "sqKey": "S1192", + "scope": "Main", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S122.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S122.html new file mode 100644 index 00000000..412d2806 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S122.html @@ -0,0 +1,16 @@ +

Why is this an issue?

+

Putting multiple statements on a single line lowers the code readability and makes debugging the code more complex.

+
+foo(); bar() // Noncompliant
+
+

Write one statement per line to improve readability.

+
+foo()
+bar()
+
+

Exceptions

+

The rule ignores control flow statements with a single nested statement.

+
+if condition { doSomething() } // Compliant by exception
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S122.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S122.json new file mode 100644 index 00000000..b71d61b5 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S122.json @@ -0,0 +1,23 @@ +{ + "title": "Statements should be on separate lines", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "FORMATTED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "1min" + }, + "tags": [ + "convention" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-122", + "sqKey": "S122", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S126.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S126.html new file mode 100644 index 00000000..19ddb56d --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S126.html @@ -0,0 +1,28 @@ +

Why is this an issue?

+

This rule applies whenever an if statement is followed by one or more else if statements; the final else if +should be followed by an else statement.

+

The requirement for a final else statement is defensive programming.

+

The else statement should either take appropriate action or contain a suitable comment as to why no action is taken. This is +consistent with the requirement to have a final default clause in a switch statement.

+

Noncompliant code example

+
+if x == 0 {
+	doSomething()
+} else if x == 1 {
+	doSomethingElse()
+}
+
+

Compliant solution

+
+if x == 0 {
+	doSomething()
+} else if x == 1 {
+	doSomethingElse()
+} else {
+	return errors.New("unsupported int")
+}
+
+

Exceptions

+

When all branches of an if-else if end with return or break, the code that comes after the +if implicitly behaves as if it was in an else clause. This rule will therefore ignore that case.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S126.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S126.json new file mode 100644 index 00000000..cfba47a7 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S126.json @@ -0,0 +1,21 @@ +{ + "title": "\"if ... else if\" constructs should end with \"else\" clauses", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-126", + "sqKey": "S126", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S131.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S131.html new file mode 100644 index 00000000..62cba97b --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S131.html @@ -0,0 +1,28 @@ +

Why is this an issue?

+

The requirement for a final default clause is defensive programming. The clause should either take appropriate action, or contain a +suitable comment as to why no action is taken.

+

Noncompliant code example

+
+switch tag { // Noncompliant - default case is missing
+case 0, 1, 2, 3:
+	foo()
+case 4, 5, 6, 7:
+	bar()
+}
+
+

Compliant solution

+
+switch tag {
+case 0, 1, 2, 3:
+	foo()
+case 4, 5, 6, 7:
+	bar()
+default:
+	qix()
+}
+
+

Resources

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S131.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S131.json new file mode 100644 index 00000000..3081f263 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S131.json @@ -0,0 +1,28 @@ +{ + "title": "\"switch\" statements should have \"default\" clauses", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "cwe" + ], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-131", + "sqKey": "S131", + "scope": "All", + "securityStandards": { + "CWE": [ + 478 + ] + }, + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1313.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1313.html new file mode 100644 index 00000000..585993c6 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1313.html @@ -0,0 +1,65 @@ +

Hardcoding IP addresses is security-sensitive. It has led in the past to the following vulnerabilities:

+ +

Today’s services have an ever-changing architecture due to their scaling and redundancy needs. It is a mistake to think that a service will always +have the same IP address. When it does change, the hardcoded IP will have to be modified too. This will have an impact on the product development, +delivery, and deployment:

+
    +
  • The developers will have to do a rapid fix every time this happens, instead of having an operation team change a configuration file.
  • +
  • It misleads to use the same address in every environment (dev, sys, qa, prod).
  • +
+

Last but not least it has an effect on application security. Attackers might be able to decompile the code and thereby discover a potentially +sensitive address. They can perform a Denial of Service attack on the service, try to get access to the system, or try to spoof the IP address to +bypass security checks. Such attacks can always be possible, but in the case of a hardcoded IP address solving the issue will take more time, which +will increase an attack’s impact.

+

Ask Yourself Whether

+

The disclosed IP address is sensitive, e.g.:

+
    +
  • Can give information to an attacker about the network topology.
  • +
  • It’s a personal (assigned to an identifiable person) IP address.
  • +
+

There is a risk if you answered yes to any of these questions.

+

Recommended Secure Coding Practices

+

Don’t hard-code the IP address in the source code, instead make it configurable with environment variables, configuration files, or a similar +approach. Alternatively, if confidentially is not required a domain name can be used since it allows to change the destination quickly without having +to rebuild the software.

+

Sensitive Code Example

+
+var (
+  ip   = "192.168.12.42"
+  port = 3333
+)
+
+SocketClient(ip, port)
+
+

Compliant Solution

+
+config, err := ReadConfig("properties.ini")
+
+ip := config["ip"]
+port := config["ip"]
+
+SocketClient(ip, port)
+
+

Exceptions

+

No issue is reported for the following cases because they are not considered sensitive:

+
    +
  • Loopback addresses 127.0.0.0/8 in CIDR notation (from 127.0.0.0 to 127.255.255.255)
  • +
  • Broadcast address 255.255.255.255
  • +
  • Non-routable address 0.0.0.0
  • +
  • Strings of the form 2.5.<number>.<number> as they often match + Object Identifiers (OID)
  • +
  • Addresses in the ranges 192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24, reserved for documentation purposes by RFC 5737
  • +
  • Addresses in the range 2001:db8::/32, reserved for documentation purposes by RFC + 3849
  • +
+

See

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1313.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1313.json new file mode 100644 index 00000000..889977b8 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1313.json @@ -0,0 +1,29 @@ +{ + "title": "Using hardcoded IP addresses is security-sensitive", + "type": "SECURITY_HOTSPOT", + "code": { + "impacts": { + "SECURITY": "LOW" + }, + "attribute": "TRUSTWORTHY" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "30min" + }, + "tags": [], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-1313", + "sqKey": "S1313", + "scope": "Main", + "securityStandards": { + "OWASP": [ + "A3" + ], + "OWASP Top 10 2021": [ + "A1" + ] + }, + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1314.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1314.html new file mode 100644 index 00000000..71ef1190 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1314.html @@ -0,0 +1,18 @@ +

Why is this an issue?

+

Integer literals starting with a zero are octal rather than decimal values. While using octal values is fully supported, most developers do not +have experience with them. They may not recognize octal values as such, mistaking them instead for decimal values.

+

Noncompliant code example

+
+func printTen() {
+	myNumber := 010 // Noncompliant. myNumber will hold 8, not 10 - was this really expected?
+	fmt.Println(myNumber)
+}
+
+

Compliant solution

+
+func printTen() {
+	myNumber := 10
+	fmt.Println(myNumber)
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1314.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1314.json new file mode 100644 index 00000000..66bbaf09 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1314.json @@ -0,0 +1,23 @@ +{ + "title": "Octal values should not be used", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "pitfall" + ], + "defaultSeverity": "Blocker", + "ruleSpecification": "RSPEC-1314", + "sqKey": "S1314", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S134.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S134.html new file mode 100644 index 00000000..133a1442 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S134.html @@ -0,0 +1,24 @@ +

Why is this an issue?

+

Nested if, for, while, switch, and try statements are key ingredients for making +what’s known as "Spaghetti code".

+

Such code is hard to read, refactor and therefore maintain.

+

Noncompliant code example

+

With the default threshold of 3:

+
+if condition1 { // Compliant - depth = 1
+	/* ... */
+	if condition2 { // Compliant - depth = 2
+		/* ... */
+		for i := 1; i <= 10; i++ { // Compliant - depth = 3, not exceeding the limit
+			/* ... */
+			if condition4 { // Noncompliant - depth = 4
+				if condition5 { // Depth = 5, exceeding the limit, but issues are only reported on depth = 4
+					/* ... */
+				}
+				return
+			}
+		}
+	}
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S134.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S134.json new file mode 100644 index 00000000..c97613e5 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S134.json @@ -0,0 +1,23 @@ +{ + "title": "Control flow statements \"if\", \"for\" and \"switch\" should not be nested too deeply", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "10min" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-134", + "sqKey": "S134", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S138.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S138.html new file mode 100644 index 00000000..9658520e --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S138.html @@ -0,0 +1,6 @@ +

Why is this an issue?

+

A function that grows too large tends to aggregate too many responsibilities.

+

Such functions inevitably become harder to understand and therefore harder to maintain.

+

Above a specific threshold, it is strongly advised to refactor into smaller functions which focus on well-defined tasks.

+

Those smaller functions will not only be easier to understand, but also probably easier to test.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S138.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S138.json new file mode 100644 index 00000000..6e8c779d --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S138.json @@ -0,0 +1,23 @@ +{ + "title": "Functions and methods should not have too many lines", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "20min" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-138", + "sqKey": "S138", + "scope": "Main", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1451.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1451.html new file mode 100644 index 00000000..470269a4 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1451.html @@ -0,0 +1,26 @@ +

Why is this an issue?

+

Each source file should start with a header stating file ownership and the license which must be used to distribute the application.

+

This rule must be fed with the header text that is expected at the beginning of every file.

+

Compliant solution

+
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2013 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * SonarQube is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1451.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1451.json new file mode 100644 index 00000000..b6b4082f --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1451.json @@ -0,0 +1,21 @@ +{ + "title": "Track lack of copyright and license headers", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "LAWFUL" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [], + "defaultSeverity": "Blocker", + "ruleSpecification": "RSPEC-1451", + "sqKey": "S1451", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1479.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1479.html new file mode 100644 index 00000000..eb61a39c --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1479.html @@ -0,0 +1,4 @@ +

Why is this an issue?

+

When switch statements have large sets of case clauses, it is usually an attempt to map two sets of data. A real map +structure would be more readable and maintainable, and should be used instead.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1479.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1479.json new file mode 100644 index 00000000..9bad731a --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1479.json @@ -0,0 +1,23 @@ +{ + "title": "\"switch\" statements should not have too many \"case\" clauses", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "30min" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1479", + "sqKey": "S1479", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1656.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1656.html new file mode 100644 index 00000000..de90dc7d --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1656.html @@ -0,0 +1,16 @@ +

Why is this an issue?

+

There is no reason to re-assign a variable to itself. Either this statement is redundant and should be removed, or the re-assignment is a mistake +and some other value or variable was intended for the assignment instead.

+

Noncompliant code example

+
+func (user *User) rename(name string) {
+  name = name  // Noncompliant
+}
+
+

Compliant solution

+
+func (user *User) rename(name string) {
+  user.name = name
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1656.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1656.json new file mode 100644 index 00000000..d3fa30a6 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1656.json @@ -0,0 +1,21 @@ +{ + "title": "Variables should not be self-assigned", + "type": "BUG", + "code": { + "impacts": { + "RELIABILITY": "MEDIUM" + }, + "attribute": "LOGICAL" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "3min" + }, + "tags": [], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1656", + "sqKey": "S1656", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1763.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1763.html new file mode 100644 index 00000000..b64a3624 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1763.html @@ -0,0 +1,23 @@ +

Once control flow has been moved out of the current code block, any subsequent statements become effectively unreachable.

+

Why is this an issue?

+

Some statements (return, break, continue, goto, switch) and throw +expressions move control flow out of the current code block. So any unlabeled statements that come after such a jump are unreachable, and either this +dead code should be removed, or the logic should be corrected.

+

Noncompliant code example

+
+func add(x, y int) int {
+	return x + y // Noncompliant
+	z := x + y // dead code
+}
+
+

Compliant solution

+
+func add(x, y int) int {
+	return x + y // Compliant
+}
+
+

Resources

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1763.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1763.json new file mode 100644 index 00000000..11f1ebbe --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1763.json @@ -0,0 +1,29 @@ +{ + "title": "All code should be reachable", + "type": "BUG", + "code": { + "impacts": { + "RELIABILITY": "MEDIUM" + }, + "attribute": "LOGICAL" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "5min" + }, + "tags": [ + "cwe", + "unused" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1763", + "sqKey": "S1763", + "scope": "Main", + "securityStandards": { + "CWE": [ + 561 + ] + }, + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1764.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1764.html new file mode 100644 index 00000000..740d6634 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1764.html @@ -0,0 +1,23 @@ +

Why is this an issue?

+

Using the same value on both sides of a binary operator is a code defect. In the case of logical operators, it is either a copy/paste error and, +therefore, a bug, or it is simply duplicated code and should be simplified. In the case of bitwise operators and most binary mathematical operators, +having the same value on both sides of an operator yields predictable results and should be simplified as well.

+

Noncompliant code example

+
+func main() {
+  v1 := (true && false) && (true && false) // Noncompliant
+}
+
+

Compliant solution

+
+func main() {
+  v1 := (true && false) // Compliant
+}
+
+

Exceptions

+

This rule ignores *, +, << and =.

+

Resources

+
    +
  • {rule:go:S1656} - Implements a check on =.
  • +
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1764.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1764.json new file mode 100644 index 00000000..31ccc4e1 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1764.json @@ -0,0 +1,21 @@ +{ + "title": "Identical expressions should not be used on both sides of a binary operator", + "type": "BUG", + "code": { + "impacts": { + "RELIABILITY": "MEDIUM" + }, + "attribute": "LOGICAL" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "2min" + }, + "tags": [], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1764", + "sqKey": "S1764", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1821.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1821.html new file mode 100644 index 00000000..72a503d9 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1821.html @@ -0,0 +1,40 @@ +

Why is this an issue?

+

Nested switch structures are difficult to understand because you can easily confuse the cases of an inner switch as +belonging to an outer statement. Therefore nested switch statements should be avoided.

+

Specifically, you should structure your code to avoid the need for nested switch statements, but if you cannot, then consider moving +the inner switch to another function.

+

Noncompliant code example

+
+func foo(x,y int) {
+	switch x {
+	case 0:
+		switch y { // Noncompliant; nested switch
+		// ...
+		}
+	case 1:
+		// ...
+	default:
+		// ...
+	}
+}
+
+

Compliant solution

+
+func foo(x,y int) {
+	switch x {
+	case 0:
+		bar(y)
+	case 1:
+		// ...
+	default:
+		// ...
+	}
+}
+
+func bar(y int) {
+	switch y {
+	// ...
+	}
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1821.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1821.json new file mode 100644 index 00000000..1111a6ff --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1821.json @@ -0,0 +1,23 @@ +{ + "title": "\"switch\" statements should not be nested", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "10min" + }, + "tags": [ + "pitfall" + ], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-1821", + "sqKey": "S1821", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1862.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1862.html new file mode 100644 index 00000000..b4c45373 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1862.html @@ -0,0 +1,42 @@ +

Why is this an issue?

+

A chain of if/else if statements is evaluated from top to bottom. At most, only one branch will be executed: the first +one with a condition that evaluates to true.

+

Therefore, duplicating a condition automatically leads to dead code. Usually, this is due to a copy/paste error. At best, it’s simply dead code and +at worst, it’s a bug that is likely to induce further bugs as the code is maintained, and obviously it could lead to unexpected behavior.

+

Noncompliant code example

+
+func example(condition1, condition2 bool) {
+  if condition1 {
+  } else if condition1 { // Noncompliant
+  }
+}
+
+
+func SwitchWithMultipleConditions(param int) {
+  switch param {
+  case 1, 2, 3:
+    fmt.Println(">1")
+  case 3, 4, 5: // Noncompliant; 3 is duplicated
+    fmt.Println("<1")
+  }
+}
+
+

Compliant solution

+
+func example(condition1, condition2 bool) {
+  if condition1 {
+  } else if condition2 { // Compliant
+  }
+}
+
+
+func SwitchWithMultipleConditions(param int) {
+  switch param {
+  case 1, 2, 3:
+    fmt.Println(">1")
+  case 4, 5: // Compliant
+    fmt.Println("<1")
+  }
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1862.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1862.json new file mode 100644 index 00000000..6046c06b --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1862.json @@ -0,0 +1,24 @@ +{ + "title": "Related \"if\/else if\" statements should not have the same condition", + "type": "BUG", + "code": { + "impacts": { + "RELIABILITY": "MEDIUM" + }, + "attribute": "LOGICAL" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "10min" + }, + "tags": [ + "unused", + "pitfall" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1862", + "sqKey": "S1862", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1871.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1871.html new file mode 100644 index 00000000..d25d7e41 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1871.html @@ -0,0 +1,43 @@ +

Why is this an issue?

+

Having two cases in a switch statement or two branches in an if chain with the same implementation is at +best duplicate code, and at worst a coding error. If the same logic is truly needed for both instances, then in an if chain they should +be combined, or for a switch, one should fall through to the other.

+

Noncompliant code example

+
+switch i {
+case 1:
+	doFirstThing()
+	doSomething()
+case 2:
+	doSomethingElse()
+case 3: // Noncompliant; duplicates case 1's implementation
+	doFirstThing()
+	doSomething()
+default:
+	doTheRest()
+}
+
+if a >= 0 && a < 10 {
+	doFirstThing()
+	doSomething()
+} else if a >= 10 && a < 20 {
+	doSomethingElse()
+} else if a >= 20 && a < 50 {
+	doFirstThing()
+	doSomething() // Noncompliant; duplicates first condition
+} else {
+	doTheRest()
+}
+
+

Exceptions

+

Blocks in an if chain or case blocks that contain a single line of code are ignored.

+
+if a == 1 {
+  doSomething()  //no issue, usually this is done on purpose to increase the readability
+} else if a == 2 {
+  doSomethingElse()
+} else {
+  doSomething()
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1871.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1871.json new file mode 100644 index 00000000..94130e8b --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1871.json @@ -0,0 +1,24 @@ +{ + "title": "Two branches in a conditional structure should not have exactly the same implementation", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "DISTINCT" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "10min" + }, + "tags": [ + "design", + "suspicious" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-1871", + "sqKey": "S1871", + "scope": "Main", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1940.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1940.html new file mode 100644 index 00000000..c54639d4 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1940.html @@ -0,0 +1,13 @@ +

Why is this an issue?

+

It is needlessly complex to invert the result of a boolean comparison. The opposite comparison should be made instead.

+

Noncompliant code example

+
+if ( !(a == 2)) { ...}  // Noncompliant
+boolean b = !(i < 10);  // Noncompliant
+
+

Compliant solution

+
+if (a != 2) { ...}
+boolean b = (i >= 10);
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1940.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1940.json new file mode 100644 index 00000000..f7fe6fdc --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S1940.json @@ -0,0 +1,23 @@ +{ + "title": "Boolean checks should not be inverted", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "CONVENTIONAL" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "2min" + }, + "tags": [ + "pitfall" + ], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-1940", + "sqKey": "S1940", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2068.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2068.html new file mode 100644 index 00000000..5d841d09 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2068.html @@ -0,0 +1,55 @@ +

Because it is easy to extract strings from an application source code or binary, credentials should not be hard-coded. This is particularly true +for applications that are distributed or that are open-source.

+

In the past, it has led to the following vulnerabilities:

+ +

Credentials should be stored outside of the code in a configuration file, a database, or a management service for secrets.

+

This rule flags instances of hard-coded credentials used in database and LDAP connections. It looks for hard-coded credentials in connection +strings, and for variable names that match any of the patterns from the provided list.

+

It’s recommended to customize the configuration of this rule with additional credential words such as "oauthToken", "secret", …​

+

Ask Yourself Whether

+
    +
  • Credentials allow access to a sensitive component like a database, a file storage, an API or a service.
  • +
  • Credentials are used in production environments.
  • +
  • Application re-distribution is required before updating the credentials.
  • +
+

There is a risk if you answered yes to any of those questions.

+

Recommended Secure Coding Practices

+
    +
  • Store the credentials in a configuration file that is not pushed to the code repository.
  • +
  • Store the credentials in a database.
  • +
  • Use your cloud provider’s service for managing secrets.
  • +
  • If a password has been disclosed through the source code: change it.
  • +
+

Sensitive Code Example

+
+func connect()  {
+  user := "root"
+  password:= "supersecret" // Sensitive
+
+  url := "login=" + user + "&passwd=" + password
+}
+
+

Compliant Solution

+
+func connect()  {
+  user := getEncryptedUser()
+  password:= getEncryptedPass() // Compliant
+
+  url := "login=" + user + "&passwd=" + password
+}
+
+

See

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2068.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2068.json new file mode 100644 index 00000000..9944be15 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2068.json @@ -0,0 +1,46 @@ +{ + "title": "Hard-coded credentials are security-sensitive", + "type": "SECURITY_HOTSPOT", + "code": { + "impacts": { + "SECURITY": "HIGH" + }, + "attribute": "TRUSTWORTHY" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "30min" + }, + "tags": [ + "cwe" + ], + "defaultSeverity": "Blocker", + "ruleSpecification": "RSPEC-2068", + "sqKey": "S2068", + "scope": "Main", + "securityStandards": { + "CWE": [ + 798, + 259 + ], + "OWASP": [ + "A2" + ], + "OWASP Top 10 2021": [ + "A7" + ], + "PCI DSS 3.2": [ + "6.5.10" + ], + "PCI DSS 4.0": [ + "6.2.4" + ], + "ASVS 4.0": [ + "2.10.4", + "3.5.2", + "6.4.1" + ] + }, + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2757.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2757.html new file mode 100644 index 00000000..6b512a21 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2757.html @@ -0,0 +1,24 @@ +

Why is this an issue?

+

Using operator pairs (=+, =-, or =!) that look like reversed single operators (+=, +-= or !=) is confusing. They compile and run but do not produce the same result as their mirrored counterpart.

+
+var target, num = -5, 3
+
+target =- num  // Noncompliant: target = -3. Is that the expected behavior?
+target =+ num // Noncompliant: target = 3
+
+

This rule raises an issue when =+, =-, or =! are used without any space between the operators and when there +is at least one whitespace after.

+

Replace the operators with a single one if that is the intention

+
+var target, num = -5, 3
+
+target -= num  // target = -8
+
+

Or fix the spacing to avoid confusion

+
+var target, num = -5, 3
+
+target = -num  // target = -3
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2757.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2757.json new file mode 100644 index 00000000..c45e5c38 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S2757.json @@ -0,0 +1,21 @@ +{ + "title": "Non-existent operators like \"\u003d+\" should not be used", + "type": "BUG", + "code": { + "impacts": { + "RELIABILITY": "MEDIUM" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "2min" + }, + "tags": [], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-2757", + "sqKey": "S2757", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3776.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3776.html new file mode 100644 index 00000000..554d55e3 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3776.html @@ -0,0 +1,14 @@ +

Why is this an issue?

+

Cognitive Complexity is a measure of how hard the control flow of a function +is to understand. Functions with high Cognitive Complexity will be difficult to maintain.

+

Resources

+

Documentation

+ +

Articles & blog posts

+ + diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3776.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3776.json new file mode 100644 index 00000000..f18253c5 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3776.json @@ -0,0 +1,25 @@ +{ + "title": "Cognitive Complexity of functions should not be too high", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "HIGH" + }, + "attribute": "FOCUSED" + }, + "status": "ready", + "remediation": { + "func": "Linear with offset", + "linearDesc": "per complexity point over the threshold", + "linearOffset": "5min", + "linearFactor": "1min" + }, + "tags": [ + "brain-overload" + ], + "defaultSeverity": "Critical", + "ruleSpecification": "RSPEC-3776", + "sqKey": "S3776", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3923.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3923.html new file mode 100644 index 00000000..18f9d009 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3923.html @@ -0,0 +1,32 @@ +

Why is this an issue?

+

Having all branches of a switch or if chain with the same implementation indicates a problem.

+

In the following code:

+
+if b == 0 {  // Noncompliant
+  doOneMoreThing()
+} else {
+  doOneMoreThing()
+}
+
+switch i {  // Noncompliant
+  case 1:
+    doSomething()
+  case 2:
+    doSomething()
+  case 3:
+    doSomething()
+  default:
+    doSomething()
+}
+
+

Either there is a copy-paste error that needs fixing or an unnecessary switch or if chain that needs removing.

+

Exceptions

+

This rule does not apply to if chains without else, nor to switch without a default clause.

+
+if b == 0 {    //no issue, this could have been done on purpose to make the code more readable
+  doSomething()
+} else if b == 1 {
+  doSomething()
+}
+
+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3923.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3923.json new file mode 100644 index 00000000..d66c5980 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S3923.json @@ -0,0 +1,21 @@ +{ + "title": "All branches in a conditional structure should not have exactly the same implementation", + "type": "BUG", + "code": { + "impacts": { + "RELIABILITY": "MEDIUM" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "15min" + }, + "tags": [], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-3923", + "sqKey": "S3923", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4144.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4144.html new file mode 100644 index 00000000..5d6ca16d --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4144.html @@ -0,0 +1,32 @@ +

Why is this an issue?

+

Two functions having the same implementation are suspicious. It might be that something else was intended. Or the duplication is intentional, which +becomes a maintenance burden.

+
+func fun1() (x, y int) {
+  a, b := 1, 2
+  b, a = a, b
+  return a, b
+}
+
+func fun2() (x, y int) {  // Noncompliant; duplicates fun1
+  a, b := 1, 2
+  b, a = a, b
+  return a, b
+}
+
+

If the identical logic is intentional, the code should be refactored to avoid duplication. For example, by having both functions call the same +function or by having one implementation invoke the other.

+
+func fun1() (x, y int) {
+  a, b := 1, 2
+  b, a = a, b
+  return a, b
+}
+
+func fun2() (x, y int) {  // Intent is clear
+  return fun1()
+}
+
+

Exceptions

+

Functions with fewer than 2 statements are ignored.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4144.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4144.json new file mode 100644 index 00000000..f13b8f83 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4144.json @@ -0,0 +1,25 @@ +{ + "title": "Functions should not have identical implementations", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "MEDIUM" + }, + "attribute": "DISTINCT" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "15min" + }, + "tags": [ + "confusing", + "duplicate", + "suspicious" + ], + "defaultSeverity": "Major", + "ruleSpecification": "RSPEC-4144", + "sqKey": "S4144", + "scope": "All", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4663.html b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4663.html new file mode 100644 index 00000000..98b9759c --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4663.html @@ -0,0 +1,11 @@ +

Why is this an issue?

+

Empty comments like the following don’t improve readability and might indicate an oversight.

+
+/*  */
+
+/*
+
+ */
+
+

A meaningful text should be added to the comment or the comment markers should be removed.

+ diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4663.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4663.json new file mode 100644 index 00000000..20a1e576 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/S4663.json @@ -0,0 +1,21 @@ +{ + "title": "Multi-line comments should not be empty", + "type": "CODE_SMELL", + "code": { + "impacts": { + "MAINTAINABILITY": "LOW" + }, + "attribute": "CLEAR" + }, + "status": "ready", + "remediation": { + "func": "Constant\/Issue", + "constantCost": "1min" + }, + "tags": [], + "defaultSeverity": "Minor", + "ruleSpecification": "RSPEC-4663", + "sqKey": "S4663", + "scope": "Main", + "quickfix": "unknown" +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/Sonar_way_profile.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/Sonar_way_profile.json new file mode 100644 index 00000000..0b4d98f7 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/go/Sonar_way_profile.json @@ -0,0 +1,30 @@ +{ + "name": "Sonar way", + "ruleKeys": [ + "ParsingError", + "S100", + "S107", + "S108", + "S117", + "S1110", + "S1125", + "S1134", + "S1135", + "S1186", + "S1192", + "S1313", + "S1479", + "S1656", + "S1763", + "S1764", + "S1862", + "S1871", + "S1940", + "S2068", + "S2757", + "S3776", + "S3923", + "S4144", + "S4663" + ] +} diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/golint/rules.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/golint/rules.json new file mode 100644 index 00000000..336d9c7e --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/golint/rules.json @@ -0,0 +1,92 @@ +[ + { + "key": "PackageComment", + "name": "Checks package comments", + "description": "This rule complains if there is no package comment, or if it is not of the right form.

See

" + }, + { + "key": "BlankImports", + "name": "Check blank imports", + "description": "This rule complains if a non-main package has blank imports that are not documented." + }, + { + "key": "Imports", + "name": "Examines import blocks", + "description": "This rule Examines usage of dot import.

See

" + }, + { + "key": "Exported", + "name": "Examines exported names", + "description": "This rule complains if any required doc comments are missing, or if they are not of the right form. It also complains if the names stutter when combined with the package name.

See

" + }, + { + "key": "Names", + "name": "Examines all names in the file", + "description": "This rule complains if any use underscores or incorrect known initialisms.

See

" + }, + { + "key": "VarDecls", + "name": "Examines variable declarations", + "description": "This rule complains about declarations with redundant LHS types that can be inferred from the RHS." + }, + { + "key": "Elses", + "name": "Examines else blocks", + "description": "This rule complains about any else block whose if block ends in a return.

See

" + }, + { + "key": "Ranges", + "name": "Examines range clauses", + "description": "This rule complains about redundant constructions." + }, + { + "key": "Errorf", + "name": "Examines errors.New and testing.Error calls", + "description": "This rule complains if its only argument is an fmt.Sprintf invocation." + }, + { + "key": "Errors", + "name": "Examines global error vars.", + "description": "This rule complains if they aren't named in the standard way.

See

" + }, + { + "key": "ErrorStrings", + "name": "Examines error strings", + "description": "This rule complains if they are capitalized or end in punctuation or a newline." + }, + { + "key": "ReceiverNames", + "name": "Examines receiver names", + "description": "This rule complains about inconsistent names used for the same type and names such as \"this\"." + }, + { + "key": "IncDec", + "name": "Examines statements that increment or decrement a variable", + "description": "This rule complains if they don't use x++ or x--." + }, + { + "key": "ErrorReturn", + "name": "Examines function declarations that return an error", + "description": "This rule complains if the error isn't the last parameter." + }, + { + "key": "UnexportedReturn", + "name": "Examines exported function declarations", + "description": "This rule complains if any return an unexported type." + }, + { + "key": "TimeNames", + "name": "Check times names", + "description": "Look for time.Duration suffix" + }, + { + "key": "ContextKeyTypes", + "name": "Checks for call expressions to context.WithValue with basic type", + "description": "This rule checks for call expressions to context.WithValue with basic types used for the key argument.

See

" + }, + { + "key": "ContextArgs", + "name": "Examines function declarations that contain an argument with a type of context.Context", + "description": "This rule complains if that argument isn't the first parameter.

See

" + } +] diff --git a/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/govet/rules.json b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/govet/rules.json new file mode 100644 index 00000000..bbfc93a3 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/org/sonar/l10n/go/rules/govet/rules.json @@ -0,0 +1,107 @@ +[ + { + "key": "asmdecl", + "name": "Check assembly against Go declarations", + "description": "Mismatches between assembly files and Go function declarations." + }, + { + "key": "assign", + "name": "Useless assignments", + "description": "Check for useless assignments." + }, + { + "key": "atomic", + "name": "Atomic mistakes", + "description": "Common mistaken usages of the sync/atomic package." + }, + { + "key": "bool", + "name": "Boolean conditions", + "description": "Mistakes involving boolean operators." + }, + { + "key": "buildtags", + "name": "check that +build tags are valid", + "description": "Badly formed or misplaced +build tags." + }, + { + "key": "cgocall", + "name": "Invalid uses of cgo", + "description": "Detect some violations of the cgo pointer passing rules." + }, + { + "key": "composites", + "name": "Unkeyed composite literals", + "description": "Composite struct literals that do not use the field-keyed syntax." + }, + { + "key": "copylocks", + "name": "Copying locks", + "description": "Locks that are erroneously passed by value." + }, + { + "key": "httpresponse", + "name": "HTTP responses used incorrectly", + "description": "Mistakes deferring a function call on an HTTP response before checking whether the error returned with the response was nil." + }, + { + "key": "lostcancel", + "name": "Failure to call the cancelation function returned by WithCancel", + "description": "The cancelation function returned by context.WithCancel, WithTimeout, and WithDeadline must be called or the new context will remain live until its parent context is cancelled. (The background context is never cancelled.)" + }, + { + "key": "methods", + "name": "Check that canonically named methods are canonically defined", + "description": "Non-standard signatures for methods with familiar names, including: Format GobEncode GobDecode MarshalJSON MarshalXML Peek ReadByte ReadFrom ReadRune Scan Seek UnmarshalJSON UnreadByte UnreadRune WriteByte WriteTo" + }, + { + "key": "nilfunc", + "name": "Nil function comparison", + "description": "Comparisons between functions and nil." + }, + { + "key": "printf", + "name": "Check printf-like invocations", + "description": "Suspicious calls to functions in the Printf family, including any functions with these names, disregarding case: Print Printf Println Fprint Fprintf Fprintln Sprint Sprintf Sprintln Error Errorf Fatal Fatalf Log Logf Panic Panicf Panicln. The -printfuncs flag can be used to redefine this list. If the function name ends with an 'f', the function is assumed to take a format descriptor string in the manner of fmt.Printf. If not, vet complains about arguments that look like format descriptor strings. It also checks for errors such as using a Writer as the first argument of Printf." + }, + { + "key": "rangeloops", + "name": "Range loop variables", + "description": "Incorrect uses of range loop variables in closures." + }, + { + "key": "shadow", + "name": "Shadowed variables", + "description": "Variables that may have been unintentionally shadowed." + }, + { + "key": "shift", + "name": "Check for useless shifts", + "description": "Shifts equal to or longer than the variable's length." + }, + { + "key": "structtags", + "name": "Check that struct field tags have canonical format and apply to exported fields as needed", + "description": "Struct tags that do not follow the format understood by reflect.StructTag.Get. Well-known encoding struct tags (json, xml) used with unexported fields." + }, + { + "key": "tests", + "name": "Check for common mistaken usages of tests/documentation examples", + "description": "Mistakes involving tests including functions with incorrect names or signatures and example tests that document identifiers not in the package." + }, + { + "key": "unreachable", + "name": "Check for unreachable code", + "description": "Check for unreachable code." + }, + { + "key": "unsafeptr", + "name": "Misuse of unsafe Pointers", + "description": "Likely incorrect uses of unsafe.Pointer to convert integers to pointers. A conversion from uintptr to unsafe.Pointer is invalid if it implies that there is a uintptr-typed word in memory that holds a pointer value, because that word will be invisible to stack copying and to the garbage collector." + }, + { + "key": "unusedresult", + "name": "Unused result of certain function calls", + "description": "Calls to well-known functions and methods that return a value that is discarded. By default, this includes functions like fmt.Errorf and fmt.Sprintf and methods like String and Error. The flags -unusedfuncs and -unusedstringmethods control the set." + } +] diff --git a/sonar-go-plugin/src/main/resources/static/documentation.md b/sonar-go-plugin/src/main/resources/static/documentation.md new file mode 100644 index 00000000..165d57f6 --- /dev/null +++ b/sonar-go-plugin/src/main/resources/static/documentation.md @@ -0,0 +1,41 @@ +--- +title: Go +key: go +--- + + + + + + + +## Prerequisites + +* SonarQube Scanner should run on a x86-64 Windows, macOS or Linux 64bits machine +* You need the [Go](https://golang.org/) installation on the scan machine only if you want to import coverage data + +## Language-Specific Properties + +You can discover and update the Go-specific [properties](/analysis/analysis-parameters/) in: Project **[Administration > General Settings > Go](/#sonarqube-admin#/admin/settings?category=go)** + +By default, all the `vendor` directories are excluded from the analysis. However, you can change the property `sonar.go.exclusions` to a different pattern if you want to force their analysis (not recommended). + +## "sonar-project.properties" Sample + +Here is a first version of a `sonar-project.properties` file, valid for a simple `Go` project: + +``` + sonar.projectKey=com.company.projectkey1 + sonar.projectName=My Project Name + + sonar.sources=. + sonar.exclusions=**/*_test.go + + sonar.tests=. + sonar.test.inclusions=**/*_test.go +``` + +## Related Pages + +* [Test Coverage & Execution](/analysis/coverage/) +* [Importing External Issues](/analysis/external-issues/) (GoVet, GoLint, GoMetaLinter) diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/checks/CodeAfterJumpGoCheckTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/checks/CodeAfterJumpGoCheckTest.java new file mode 100644 index 00000000..1d3f78fd --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/checks/CodeAfterJumpGoCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +import org.junit.jupiter.api.Test; + +class CodeAfterJumpGoCheckTest { + @Test + void test() { + GoVerifier.verify("CodeAfterJumpGoCheck.go", new CodeAfterJumpGoCheck()); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/checks/DuplicateBranchGoCheckTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/checks/DuplicateBranchGoCheckTest.java new file mode 100644 index 00000000..80144530 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/checks/DuplicateBranchGoCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +import org.junit.jupiter.api.Test; + +class DuplicateBranchGoCheckTest { + @Test + void test() { + GoVerifier.verify("DuplicateBranchGoCheck.go", new DuplicateBranchGoCheck()); + } +} \ No newline at end of file diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/checks/GoVerifier.java b/sonar-go-plugin/src/test/java/org/sonar/go/checks/GoVerifier.java new file mode 100644 index 00000000..863f38b4 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/checks/GoVerifier.java @@ -0,0 +1,36 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +import org.sonar.go.converter.GoConverter; +import org.sonarsource.slang.api.ASTConverter; +import org.sonarsource.slang.checks.api.SlangCheck; + +import java.nio.file.Path; +import java.nio.file.Paths; + +public class GoVerifier { + private static final Path BASE_DIR = Paths.get("src", "test", "resources", "checks"); + private static final ASTConverter CONVERTER = new GoConverter(Paths.get("build", "tmp").toFile()); + + public static void verify(String fileName, SlangCheck check) { + org.sonarsource.slang.testing.Verifier.verify(CONVERTER, BASE_DIR.resolve(fileName), check); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/checks/OneStatementPerLineGoCheckTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/checks/OneStatementPerLineGoCheckTest.java new file mode 100644 index 00000000..6d05d517 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/checks/OneStatementPerLineGoCheckTest.java @@ -0,0 +1,29 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.checks; + +import org.junit.jupiter.api.Test; + +class OneStatementPerLineGoCheckTest { + @Test + void test() { + GoVerifier.verify("OneStatementPerLineGoCheck.go", new OneStatementPerLineGoCheck()); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/checks/README.md b/sonar-go-plugin/src/test/java/org/sonar/go/checks/README.md new file mode 100644 index 00000000..1ad1a21b --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/checks/README.md @@ -0,0 +1 @@ +Here language-specific check implementations should be stored. \ No newline at end of file diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/converter/GoConverterTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/converter/GoConverterTest.java new file mode 100644 index 00000000..eb0f0867 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/converter/GoConverterTest.java @@ -0,0 +1,179 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.converter; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.api.BlockTree; +import org.sonarsource.slang.api.ClassDeclarationTree; +import org.sonarsource.slang.api.FunctionDeclarationTree; +import org.sonarsource.slang.api.IntegerLiteralTree; +import org.sonarsource.slang.api.LoopTree; +import org.sonarsource.slang.api.NativeTree; +import org.sonarsource.slang.api.ParseException; +import org.sonarsource.slang.api.ReturnTree; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; +import org.sonarsource.slang.api.VariableDeclarationTree; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.sonar.go.converter.GoConverter.DefaultCommand.getExecutableForCurrentOS; + +class GoConverterTest { + + @Test + void test_parse_return() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + Tree tree = converter.parse("package main\nfunc foo() {return 42}"); + List returnList = getReturnsList(tree); + assertThat(returnList).hasSize(1); + + checkIntegerValue(returnList.get(0), "42"); + } + + @Test + void test_parse_binary_notation() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + Tree tree = converter.parse("package main\nfunc foo() {return 0b_0010_1010}"); + List returnList = getReturnsList(tree); + assertThat(returnList).hasSize(1); + + checkIntegerValue(returnList.get(0), "_0010_1010"); + } + + @Test + void test_parse_imaginary_literals() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + Tree tree = converter.parse("package main\nfunc foo() {return 6.67428e-11i}"); + List returnList = getReturnsList(tree); + assertThat(returnList).hasSize(1); + } + + @Test + void test_parse_embed_overlapping_interfaces() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + Tree tree = converter.parse("package main\ntype A interface{\n DoX() string\n}\ntype B interface{\n DoX() \n}\ntype AB interface{\n A\n B\n}"); + List classList = tree.descendants() + .filter(t -> t instanceof ClassDeclarationTree) + .collect(Collectors.toList()); + assertThat(classList).hasSize(3); + } + + @Test + void test_parse_infinite_for() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + Tree tree = converter.parse("package main\nfunc foo() {for {}}"); + List returnList = tree.descendants().filter(t -> t instanceof LoopTree).collect(Collectors.toList()); + assertThat(returnList).hasSize(1); + } + + @Test + void test_parse_generics() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + Tree tree = converter.parse("package main\nfunc f1[T any]() {}\nfunc f2() {\nf:=f1[string]}"); + List functions = tree.descendants().filter(t -> t instanceof FunctionDeclarationTree).collect(Collectors.toList()); + assertThat(functions).hasSize(2); + + List f1Children = functions.get(0).children(); + assertThat(f1Children).hasSize(3); + Tree typeParams = f1Children.get(2); + assertThat(typeParams).isInstanceOf(NativeTree.class); + assertThat(((NativeTree) typeParams).nativeKind()).hasToString("TypeParams(FieldList)"); + + List f2Children = functions.get(1).children(); + Tree f = ((BlockTree) f2Children.get(1)).statementOrExpressions().get(0); + assertThat(f).isInstanceOf(VariableDeclarationTree.class); + } + + @Test + void parse_error() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + ParseException e = assertThrows(ParseException.class, + () -> converter.parse("$!#@")); + assertThat(e).hasMessage("Go parser external process returned non-zero exit value: 2"); + } + + @Test + void invalid_command() { + GoConverter.Command command = mock(GoConverter.Command.class); + when(command.getCommand()).thenReturn(Collections.singletonList("invalid-command")); + GoConverter converter = new GoConverter(command); + ParseException e = assertThrows(ParseException.class, + () -> converter.parse("package main\nfunc foo() {}")); + assertThat(e).hasMessageContaining("Cannot run program \"invalid-command\""); + } + + @Test + void parse_accepted_big_file() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + String code = "package main\n" + + "func foo() {\n" + + "}\n"; + String bigCode = code + new String(new char[700_000 - code.length()]).replace("\0", "\n"); + Tree tree = converter.parse(bigCode); + assertThat(tree).isInstanceOf(TopLevelTree.class); + } + + @Test + void parse_rejected_big_file() { + GoConverter converter = new GoConverter(Paths.get("build", "tmp").toFile()); + String code = "package main\n" + + "func foo() {\n" + + "}\n"; + String bigCode = code + new String(new char[1_500_000]).replace("\0", "\n"); + ParseException e = assertThrows(ParseException.class, + () -> converter.parse(bigCode)); + assertThat(e).hasMessage("The file size is too big and should be excluded, its size is 1500028 (maximum allowed is 1500000 bytes)"); + } + + @Test + void load_invalid_executable_path() throws IOException { + IllegalStateException e = assertThrows(IllegalStateException.class, + () -> GoConverter.DefaultCommand.getBytesFromResource("invalid-exe-path")); + assertThat(e).hasMessage("invalid-exe-path binary not found on class path"); + } + + @Test + void executable_for_current_os() { + assertThat(getExecutableForCurrentOS("Linux", "x86_64")).isEqualTo("sonar-go-to-slang-linux-amd64"); + assertThat(getExecutableForCurrentOS("Windows 10", "x86_64")).isEqualTo("sonar-go-to-slang-windows-amd64.exe"); + assertThat(getExecutableForCurrentOS("Mac OS X", "x86_64")).isEqualTo("sonar-go-to-slang-darwin-amd64"); + assertThat(getExecutableForCurrentOS("Mac OS X", "aarch64")).isEqualTo("sonar-go-to-slang-darwin-arm64"); + } + + private List getReturnsList(Tree tree) { + return tree.descendants() + .filter(t -> t instanceof ReturnTree) + .map(ReturnTree.class::cast) + .collect(Collectors.toList()); + } + + private void checkIntegerValue(ReturnTree returnTree, String s) { + IntegerLiteralTree integerLiteralTree = (IntegerLiteralTree) returnTree.body(); + assertThat(integerLiteralTree.getNumericPart()).isEqualTo(s); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/coverage/GoCoverSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/coverage/GoCoverSensorTest.java new file mode 100644 index 00000000..73e236f2 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/coverage/GoCoverSensorTest.java @@ -0,0 +1,369 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.coverage; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.Stream; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.config.internal.MapSettings; +import org.sonarsource.slang.testing.ThreadLocalLogTester; +import org.sonar.go.coverage.GoCoverSensor.Coverage; +import org.sonar.go.coverage.GoCoverSensor.CoverageStat; +import org.sonar.go.coverage.GoCoverSensor.FileCoverage; +import org.sonar.go.coverage.GoCoverSensor.LineCoverage; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class GoCoverSensorTest { + + static final Path COVERAGE_DIR = Paths.get("src", "test", "resources", "coverage"); + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + + @Test + void test_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + GoCoverSensor coverSensor = new GoCoverSensor(); + coverSensor.describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Go Cover sensor for Go coverage"); + } + + @Test + void test_failure() { + SensorContextTester context = SensorContextTester.create(COVERAGE_DIR); + context.settings().setProperty("sonar.go.coverage.reportPaths", "invalid-coverage-path.out"); + GoCoverSensor coverSensor = new GoCoverSensor(); + coverSensor.execute(context); + assertThat(logTester.logs(Level.ERROR)) + .containsExactly("Coverage report can't be loaded, report file not found, ignoring this file invalid-coverage-path.out."); + } + + @Test + void mode_line() { + Predicate regexp = (line) -> GoCoverSensor.MODE_LINE_REGEXP.matcher(line).matches(); + assertThat(regexp.test("mode: set")).isTrue(); + assertThat(regexp.test("mode: count")).isTrue(); + assertThat(regexp.test("mode: atomic")).isTrue(); + assertThat(regexp.test("my-app/my-app.go:3.2,3.10 1 1")).isFalse(); + } + + @Test + void line_regexp() { + Predicate regexp = (line) -> GoCoverSensor.COVERAGE_LINE_REGEXP.matcher(line).matches(); + assertThat(regexp.test("my-app/my-app.go:3.2,3.10 1 1")).isTrue(); + assertThat(regexp.test("_/my-app/my-app.go:3.2,3.10 1 21")).isTrue(); + assertThat(regexp.test("my-app\\my-app.go:3.2,3.10 1 0")).isTrue(); + assertThat(regexp.test("_\\C_\\my-app\\my-app.go:3.2,3.10 1 42")).isTrue(); + assertThat(regexp.test("mode: set")).isFalse(); + } + + @Test + void coverage_stat() { + CoverageStat coverage = new CoverageStat(2, "_/my-app/my-app.go:3.10,4.5 2 234"); + assertThat(coverage.filePath).isEqualTo("_/my-app/my-app.go"); + assertThat(coverage.startLine).isEqualTo(3); + assertThat(coverage.startCol).isEqualTo(10); + assertThat(coverage.endLine).isEqualTo(4); + assertThat(coverage.endCol).isEqualTo(5); + // numStmt is not parsed because not required. + assertThat(coverage.count).isEqualTo(234); + + assertThatThrownBy(() -> new CoverageStat(42, "invalid") ) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid go coverage at line 42"); + } + + @Test + void line_coverage() { + LineCoverage line = new LineCoverage(); + assertThat(line.hits).isZero(); + + line.add(new CoverageStat(2, "main.go:2.2,2.5 1 0")); + assertThat(line.hits).isZero(); + + line.add(new CoverageStat(2, "main.go:2.2,2.5 1 3")); + assertThat(line.hits).isEqualTo(3); + + line.add(new CoverageStat(2, "main.go:2.2,2.5 1 2")); + assertThat(line.hits).isEqualTo(5); + + line.add(new CoverageStat(2, "main.go:2.8,2.10 1 0")); + assertThat(line.hits).isEqualTo(5); + } + + @Test + void line_coverage_over_flow() { + LineCoverage line = new LineCoverage(); + // hits is greater than Integer.MAX_VALUE + line.add(new CoverageStat(2, "main.go:2.2,2.5 1 " + + (((long)Integer.MAX_VALUE) + 1))); + assertThat(line.hits).isEqualTo(Integer.MAX_VALUE); + + LineCoverage lineWithTwoStats = new LineCoverage(); + // hits is greater than Integer.MAX_VALUE + lineWithTwoStats.add(new CoverageStat(2, "main.go:2.2,2.5 1 " + (Integer.MAX_VALUE - 1))); + lineWithTwoStats.add(new CoverageStat(2, "main.go:2.2,2.5 1 2")); + assertThat(line.hits).isEqualTo(Integer.MAX_VALUE); + } + + @Test + void line_coverage_do_not_parse_num_statement() { + LineCoverage line = new LineCoverage(); + line.add(new CoverageStat(2, "main.go:2.2,2.5 2650701153 0")); + assertThat(line.hits).isZero(); + } + + @Test + void file_coverage() throws Exception { + List coverageStats = Arrays.asList( + new CoverageStat(2, "cover.go:4.11,6.3 1 3"), + new CoverageStat(3, "cover.go:6.3,8.3 1 0")); + FileCoverage file = new FileCoverage(coverageStats, Files.readAllLines(COVERAGE_DIR.resolve("cover.go"))); + + assertThat(file.lineMap.keySet()).containsExactlyInAnyOrder(5, 6, 7); + assertThat(file.lineMap.get(4)).isNull(); + assertThat(file.lineMap.get(5).hits).isEqualTo(3); + assertThat(file.lineMap.get(6).hits).isZero(); + assertThat(file.lineMap.get(7).hits).isZero(); + assertThat(file.lineMap.get(8)).isNull(); + } + + @Test + void file_coverage_empty_lines() throws Exception { + final String fileName = "cover_empty_lines.go"; + List coverageStats = Collections.singletonList(new CoverageStat(2, fileName + ":3.28,9.2 2 1")); + FileCoverage file = new FileCoverage(coverageStats, Files.readAllLines(COVERAGE_DIR.resolve(fileName))); + + assertThat(file.lineMap.keySet()).containsExactlyInAnyOrder(5, 7); + } + + @Test + void coverage() { + GoPathContext linuxContext = new GoPathContext('/', ":", "/home/paul/go"); + Coverage coverage = new Coverage(linuxContext); + coverage.add(new CoverageStat(2, "main.go:2.2,2.5 1 1")); + coverage.add(new CoverageStat(3, "main.go:4.2,4.7 1 0")); + coverage.add(new CoverageStat(4, "other.go:3.2,4.12 1 1")); + assertThat(coverage.fileMap.keySet()).containsExactlyInAnyOrder("/home/paul/go/src/main.go", "/home/paul/go/src/other.go"); + List coverageStats = coverage.fileMap.get("/home/paul/go/src/main.go"); + FileCoverage fileCoverage = new FileCoverage(coverageStats, null); + assertThat(fileCoverage.lineMap.keySet()).containsExactlyInAnyOrder(2, 4); + assertThat(new FileCoverage(coverage.fileMap.get("/home/paul/go/src/other.go"), null).lineMap.keySet()).containsExactlyInAnyOrder(3, 4); + } + + @Test + void parse_coverage_linux_relative() { + Path coverageFile = COVERAGE_DIR.resolve("coverage.linux.relative.out"); + GoPathContext linuxContext = new GoPathContext('/', ":", "/home/paul/go"); + String coverPath = "/home/paul/go/src/github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go"; + assertCoverGo(coverageFile, linuxContext, coverPath); + } + + @Test + void parse_coverage_linux_absolute() { + Path coverageFile = COVERAGE_DIR.resolve("coverage.linux.absolute.out"); + GoPathContext linuxContext = new GoPathContext('/', ":", "/home/paul/go"); + String coverPath = "/home/paul/dev/github/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go"; + assertCoverGo(coverageFile, linuxContext, coverPath); + } + + @Test + void parse_coverage_windows_relative() { + Path coverageFile = COVERAGE_DIR.resolve("coverage.win.relative.out"); + GoPathContext windowsContext = new GoPathContext('\\', ";", "C:\\Users\\paul\\go"); + String coverPath = "C:\\Users\\paul\\go\\src\\github.com\\SonarSource\\slang\\sonar-go-plugin\\src\\test\\resources\\coverage\\cover.go"; + assertCoverGo(coverageFile, windowsContext, coverPath); + } + + @Test + void parse_coverage_windows_absolute() { + Path coverageFile = COVERAGE_DIR.resolve("coverage.win.absolute.out"); + GoPathContext windowsContext = new GoPathContext('\\', ";", "C:\\Users\\paul\\go"); + String coverPath = "C:\\Users\\paul\\dev\\github\\SonarSource\\slang\\sonar-go-plugin\\src\\test\\resources\\coverage\\cover.go"; + assertCoverGo(coverageFile, windowsContext, coverPath); + } + + @Test + void parse_coverage_one_broken_line() { + Path coverageFile = COVERAGE_DIR.resolve("coverage.one.broken.line.out"); + GoPathContext linuxContext = new GoPathContext('/', ":", "/home/paul/go"); + String coverPath = "/home/paul/go/src/github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go"; + assertCoverGo(coverageFile, linuxContext, coverPath); + + assertThat(logTester.logs(Level.DEBUG)) + .containsExactly("Ignoring line in coverage report: Invalid go coverage at line 7."); + } + + @Test + void get_report_paths() { + SensorContextTester context = SensorContextTester.create(COVERAGE_DIR); + context.setSettings(new MapSettings()); + Path coverageFile1 = COVERAGE_DIR.resolve("coverage.linux.relative.out").toAbsolutePath(); + context.settings().setProperty("sonar.go.coverage.reportPaths", + coverageFile1 + ",coverage.linux.absolute.out"); + Stream reportPaths = GoCoverSensor.getReportPaths(context); + assertThat(reportPaths).containsExactlyInAnyOrder( + coverageFile1, + Paths.get("src", "test", "resources", "coverage", "coverage.linux.absolute.out")); + } + + @Test + void get_report_paths_with_wildcards() { + SensorContextTester context = SensorContextTester.create(COVERAGE_DIR); + context.setSettings(new MapSettings()); + context.settings().setProperty("sonar.go.coverage.reportPaths", + "*.absolute.out,glob" + File.separator +"*.out, test*" + File.separator +"*.out, coverage?.out"); + Stream reportPaths = GoCoverSensor.getReportPaths(context); + assertThat(reportPaths).containsExactlyInAnyOrder( + Paths.get("src", "test", "resources", "coverage", "coverage.linux.absolute.out"), + Paths.get("src", "test", "resources", "coverage", "coverage.win.absolute.out"), + Paths.get("src", "test", "resources", "coverage", "glob", "coverage.glob.out"), + Paths.get("src", "test", "resources", "coverage", "test1", "coverage.out"), + Paths.get("src", "test", "resources", "coverage", "coverage1.out")); + + context.settings().setProperty("sonar.go.coverage.reportPaths", + "**" + File.separator +"coverage.glob.out"); + Stream reportPaths2 = GoCoverSensor.getReportPaths(context); + assertThat(reportPaths2).containsExactlyInAnyOrder( + Paths.get("src", "test", "resources", "coverage", "glob", "coverage.glob.out")); + } + + @Test + void should_continue_if_parsing_fails() { + SensorContextTester context = SensorContextTester.create(COVERAGE_DIR); + context.setSettings(new MapSettings()); + context.settings().setProperty("sonar.go.coverage.reportPaths", + "test1" + File.separator + "coverage.out, coverage.relative.out"); + Path baseDir = COVERAGE_DIR.toAbsolutePath(); + GoPathContext goContext = new GoPathContext(File.separatorChar, File.pathSeparator, baseDir.toString()); + GoCoverSensor sensor = new GoCoverSensor(); + sensor.execute(context, goContext); + assertThat(logTester.logs(Level.ERROR)).hasSize(1); + assertThat(logTester.logs(Level.ERROR).get(0)).endsWith("coverage.out: Invalid go coverage, expect 'mode:' on the first line."); + } + + @Test + void upload_reports() throws IOException { + String fileName = "cover.go"; + SensorContextTester context = setUpContext(fileName, "coverage.relative.out"); + String fileKey = "moduleKey:" + fileName; + assertThat(context.lineHits(fileKey, 3)).isNull(); + assertThat(context.lineHits(fileKey, 4)).isEqualTo(1); + assertThat(context.lineHits(fileKey, 5)).isEqualTo(2); + assertThat(context.conditions(fileKey, 5)).isNull(); + assertThat(context.coveredConditions(fileKey, 5)).isNull(); + assertThat(context.lineHits(fileKey, 6)).isZero(); + assertThat(context.lineHits(fileKey, 7)).isZero(); + assertThat(context.lineHits(fileKey, 8)).isNull(); + } + + @Test + void coverage_fuzzy_inputfile() throws Exception { + String fileName = "cover.go"; + SensorContextTester context = setUpContext(fileName, "coverage.fuzzy.out"); + String fileKey = "moduleKey:" + fileName; + assertThat(context.lineHits(fileKey, 3)).isNull(); + assertThat(context.lineHits(fileKey, 4)).isEqualTo(1); + assertThat(context.lineHits(fileKey, 5)).isEqualTo(2); + assertThat(context.conditions(fileKey, 5)).isNull(); + assertThat(context.coveredConditions(fileKey, 5)).isNull(); + assertThat(context.lineHits(fileKey, 6)).isZero(); + assertThat(context.lineHits(fileKey, 7)).isZero(); + assertThat(context.lineHits(fileKey, 8)).isNull(); + + String ignoredFileLog = "File 'doesntexists.go' is not included in the project, ignoring coverage"; + assertThat(logTester.logs(Level.WARN)).contains(ignoredFileLog); + } + + @Test + void coverage_switch_case() throws Exception { + String fileName = "coverage.switch.go"; + SensorContextTester context = setUpContext(fileName, "coverage.switch.out"); + String fileKey = "moduleKey:" + fileName; + // Opening brace of function should not be included into the switch + assertThat(context.lineHits(fileKey, 3)).isNull(); + assertThat(context.lineHits(fileKey, 4)).isEqualTo(1); + // Switch case should not be counted + assertThat(context.lineHits(fileKey, 5)).isNull(); + assertThat(context.lineHits(fileKey, 6)).isEqualTo(1); + assertThat(context.lineHits(fileKey, 7)).isNull(); + assertThat(context.lineHits(fileKey, 8)).isZero(); + assertThat(context.lineHits(fileKey, 9)).isNull(); + assertThat(context.lineHits(fileKey, 10)).isZero(); + assertThat(context.lineHits(fileKey, 11)).isNull(); + assertThat(context.lineHits(fileKey, 12)).isZero(); + assertThat(context.lineHits(fileKey, 13)).isNull(); + assertThat(context.lineHits(fileKey, 14)).isZero(); + } + + private SensorContextTester setUpContext(String fileName, String coverageFile) throws IOException { + Path baseDir = COVERAGE_DIR.toAbsolutePath(); + SensorContextTester context = SensorContextTester.create(baseDir); + context.setSettings(new MapSettings()); + context.settings().setProperty("sonar.go.coverage.reportPaths", coverageFile); + Path goFilePath = baseDir.resolve(fileName); + String content = new String(Files.readAllBytes(goFilePath), UTF_8); + context.fileSystem().add(TestInputFileBuilder.create("moduleKey", baseDir.toFile(), goFilePath.toFile()) + .setLanguage("go") + .setType(InputFile.Type.MAIN) + .initMetadata(content) + .setContents(content) + .build()); + GoPathContext goContext = new GoPathContext(File.separatorChar, File.pathSeparator, ""); + GoCoverSensor sensor = new GoCoverSensor(); + sensor.execute(context, goContext); + return context; + } + + private void assertCoverGo(Path coverageFile, GoPathContext goContext, String absolutePath) { + Coverage coverage = new Coverage(goContext); + GoCoverSensor.parse(coverageFile, coverage); + assertThat(coverage.fileMap.keySet()).containsExactlyInAnyOrder(absolutePath); + List coverageStats = coverage.fileMap.get(absolutePath); + FileCoverage fileCoverage = new FileCoverage(coverageStats, null); + assertThat(fileCoverage.lineMap.keySet()).containsExactlyInAnyOrder(3, 4, 5, 6, 7, 8); + assertThat(fileCoverage.lineMap.get(2)).isNull(); + assertThat(fileCoverage.lineMap.get(3).hits).isEqualTo(1); + assertThat(fileCoverage.lineMap.get(4).hits).isEqualTo(2); + assertThat(fileCoverage.lineMap.get(5).hits).isEqualTo(2); + assertThat(fileCoverage.lineMap.get(6).hits).isZero(); + assertThat(fileCoverage.lineMap.get(7).hits).isZero(); + assertThat(fileCoverage.lineMap.get(8).hits).isZero(); + assertThat(fileCoverage.lineMap.get(9)).isNull(); + } + +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/coverage/GoPathContextTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/coverage/GoPathContextTest.java new file mode 100644 index 00000000..e5a81c27 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/coverage/GoPathContextTest.java @@ -0,0 +1,176 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.coverage; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.BeforeAll; + +import static org.assertj.core.api.Assertions.assertThat; + +class GoPathContextTest { + + static String[] tmpDirWithFile = new String[2]; + static String[] existingFileAbsolutePath = new String[2]; + static String tmpDirWithoutFile; + static String invalidTmpDir; + + @BeforeAll + static void prepare_temporary_folder() throws IOException { + String[] suffixes = {"0", "1"}; + for (int i = 0; i < suffixes.length; i++) { + String suffix = suffixes[i]; + Path dirWithFile = Files.createTempDirectory("tmp_dir_with_file_" + suffix); + dirWithFile.toFile().deleteOnExit(); + Path srcDir = dirWithFile.resolve("src"); + Files.createDirectory(srcDir); + Path existingFile = srcDir.resolve("file.go"); + Files.createFile(existingFile); + Path packageDir = dirWithFile.resolve("my-package"); + Files.createDirectory(packageDir); + tmpDirWithFile[i] = dirWithFile.toAbsolutePath().toString(); + existingFileAbsolutePath[i] = existingFile.toAbsolutePath().toString(); + } + + Path dirWithoutFile = Files.createTempDirectory("tmp_dir_without_file"); + dirWithoutFile.toFile().deleteOnExit(); + Files.createDirectory(dirWithoutFile.resolve("src")); + + tmpDirWithoutFile = dirWithoutFile.toAbsolutePath().toString(); + invalidTmpDir = Paths.get("directory", "not", "found").toString(); + } + + @Test + void concat_linux() { + GoPathContext context = new GoPathContext('/', ":", "/home/paul/go"); + assertThat(context.concat("/home", "paul")).isEqualTo("/home/paul"); + assertThat(context.concat("/home/", "paul")).isEqualTo("/home/paul"); + assertThat(context.concat("/", "paul")).isEqualTo("/paul"); + assertThat(context.concat("", "paul")).isEqualTo("paul"); + } + + @Test + void concat_windows() { + GoPathContext context = new GoPathContext('\\', ";", "C:\\Users\\paul\\go"); + assertThat(context.concat("C:\\Users", "paul")).isEqualTo("C:\\Users\\paul"); + assertThat(context.concat("C:\\Users\\", "paul")).isEqualTo("C:\\Users\\paul"); + assertThat(context.concat("C:\\", "paul")).isEqualTo("C:\\paul"); + assertThat(context.concat("", "paul")).isEqualTo("paul"); + } + + @Test + void multiple_go_path_entries_linux() { + GoPathContext context = new GoPathContext('/', ":", ":/home/paul/go:::/home/luc/go::"); + assertThat(context.goSrcPathList).containsExactly("/home/paul/go/src", "/home/luc/go/src"); + } + + @Test + void multiple_go_path_entries_windows() { + GoPathContext context = new GoPathContext('\\', ";", ";;C:\\Users\\paul\\go;C:\\Users\\luc\\go"); + assertThat(context.goSrcPathList).containsExactly("C:\\Users\\paul\\go\\src", "C:\\Users\\luc\\go\\src"); + } + + @Test + void resolve_existing_file() { + String goPath = tmpDirWithFile[0]; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + assertThat(context.resolve("file.go")).isEqualTo(existingFileAbsolutePath[0]); + assertThat(context.resolvedPaths.keySet()).containsExactly("file.go"); + assertThat(context.resolvedPaths.values()).containsExactly(existingFileAbsolutePath[0]); + } + + @Test + void resolve_existing_directory() { + String goPath = tmpDirWithFile[0]; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + String expected = tmpDirWithFile[0] + File.separatorChar + "src" + File.separatorChar + "my-package"; + assertThat(context.resolve("my-package")).isEqualTo(expected); + } + + @Test + void resolve_existing_file_multiple_go_path_entries() { + String goPath = tmpDirWithFile[0] + File.pathSeparator + tmpDirWithFile[1]; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + assertThat(context.resolve("file.go")).isEqualTo(existingFileAbsolutePath[0]); + assertThat(context.resolvedPaths.keySet()).containsExactly("file.go"); + assertThat(context.resolvedPaths.values()).containsExactly(existingFileAbsolutePath[0]); + } + + @Test + void resolve_existing_file_in_the_first_go_path_entry() { + String goPath = tmpDirWithFile[0] + File.pathSeparator + tmpDirWithoutFile + File.pathSeparator + invalidTmpDir; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + assertThat(context.resolve("file.go")).isEqualTo(existingFileAbsolutePath[0]); + } + + @Test + void resolve_existing_file_in_the_last_go_path_entry() { + String goPath = invalidTmpDir + File.pathSeparator + tmpDirWithoutFile + File.pathSeparator + tmpDirWithFile[0]; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + assertThat(context.resolve("file.go")).isEqualTo(existingFileAbsolutePath[0]); + } + + @Test + void resolve_none_existing_file_with_multiple_go_path_entries() { + String goPath = tmpDirWithoutFile + File.pathSeparator + invalidTmpDir; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + String expected = tmpDirWithoutFile + File.separatorChar + "src" + File.separatorChar + "file.go"; + assertThat(context.resolve("file.go")).isEqualTo(expected); + } + + @Test + void resolve_none_existing_file_with_empty_go_path() { + String goPath = File.pathSeparator + File.pathSeparator; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + assertThat(context.resolve("file.go")).isEqualTo("file.go"); + } + + @Test + void resolve_none_existing_file_with_null_go_path() { + String goPath = null; + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, goPath); + assertThat(context.resolve("file.go")).isEqualTo("file.go"); + } + + @Test + void resolve_absolute_path_linux() { + GoPathContext context = new GoPathContext('/', ":", "/home/paul/go"); + assertThat(context.resolve("_/my-app/my-app.go")).isEqualTo("/my-app/my-app.go"); + } + + @Test + void resolve_absolute_path_windows() { + GoPathContext context = new GoPathContext('\\', ";", "C:\\Users\\paul\\go"); + assertThat(context.resolve("_\\C_\\my-app\\my-app.go")).isEqualTo("C:\\my-app\\my-app.go"); + } + + @Test + void resolve_absolute_path() throws IOException { + File file = Files.createTempFile("temp_file", null).toFile(); + file.deleteOnExit(); + + GoPathContext context = new GoPathContext(File.separatorChar, File.pathSeparator, "/home/paul/go"); + assertThat(context.resolve(file.getAbsolutePath())).isEqualTo(file.getAbsolutePath()); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/AbstractReportSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/AbstractReportSensorTest.java new file mode 100644 index 00000000..76cfaa9a --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/AbstractReportSensorTest.java @@ -0,0 +1,57 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.io.File; +import javax.annotation.Nullable; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.notifications.AnalysisWarnings; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; + +class AbstractReportSensorTest { + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void report_consumer_logs_io_exception() { + AbstractReportSensor sensor = new TestReportSensor(mock(AnalysisWarnings.class)); + sensor.reportConsumer(mock(SensorContext.class)).accept(new File("invalid-file.txt")); + assertThat(logTester.logs(Level.ERROR)).containsExactly("TestReportSensor: No issues information will be saved as the report file 'invalid-file.txt' can't be read."); + } + + static class TestReportSensor extends AbstractReportSensor { + TestReportSensor(AnalysisWarnings analysisWarnings) { + super(analysisWarnings, "propertyKey", "propertyName", "configurationKey"); + } + + @Nullable + @Override + ExternalIssue parse(String line) { + throw new UnsupportedOperationException(); + } + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/ExternalLinterSensorHelper.java b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/ExternalLinterSensorHelper.java new file mode 100644 index 00000000..2b3696c3 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/ExternalLinterSensorHelper.java @@ -0,0 +1,68 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.Sensor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.go.plugin.GoLanguage; + +public class ExternalLinterSensorHelper { + + static final Path REPORT_BASE_PATH = Paths.get("src", "test", "resources", "externalreport").toAbsolutePath(); + + static List executeSensor(Sensor sensor, SensorContextTester context) { + sensor.execute(context); + return new ArrayList<>(context.allExternalIssues()); + } + + static SensorContextTester createContext() throws IOException { + Path workDir = Files.createTempDirectory("gotemp"); + workDir.toFile().deleteOnExit(); + Path projectDir = Files.createTempDirectory("goproject"); + projectDir.toFile().deleteOnExit(); + SensorContextTester context = SensorContextTester.create(workDir); + context.fileSystem().setWorkDir(workDir); + Path filePath = projectDir.resolve("main.go"); + InputFile mainInputFile = TestInputFileBuilder.create("module", projectDir.toFile(), filePath.toFile()) + .setCharset(StandardCharsets.UTF_8) + .setLanguage(GoLanguage.KEY) + .setContents("package main\n" + + "import \"fmt\"\n" + + "func main() {\n" + + " fmt.Println(\"Hello\")\n" + + "}\n") + .setType(InputFile.Type.MAIN) + .build(); + context.fileSystem().add(mainInputFile); + return context; + } + +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoLintReportSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoLintReportSensorTest.java new file mode 100644 index 00000000..b0082422 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoLintReportSensorTest.java @@ -0,0 +1,173 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.api.rules.RuleType; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.go.externalreport.AbstractReportSensor.GENERIC_ISSUE_KEY; +import static org.sonar.go.externalreport.ExternalLinterSensorHelper.REPORT_BASE_PATH; + +class GoLintReportSensorTest { + + private final List analysisWarnings = new ArrayList<>(); + + @BeforeEach + void setup() { + analysisWarnings.clear(); + } + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void test_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + goLintReportSensor().describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Import of Golint issues"); + assertThat(sensorDescriptor.languages()).containsOnly("go"); + } + + @Test + void issues_with_sonarqube() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golint.reportPaths", REPORT_BASE_PATH.resolve("golint-report.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goLintReportSensor(), context); + assertThat(externalIssues).hasSize(2); + + ExternalIssue first = externalIssues.get(0); + assertThat(first.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + assertThat(first.ruleKey().repository()).isEqualTo("external_golint"); + assertThat(first.ruleKey().rule()).isEqualTo("PackageComment"); + assertThat(first.primaryLocation().message()).isEqualTo("package comment should be of the form \"Package samples ...\""); + assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(1); + + ExternalIssue second = externalIssues.get(1); + assertThat(second.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(second.severity()).isEqualTo(Severity.MAJOR); + assertThat(second.ruleKey().repository()).isEqualTo("external_golint"); + assertThat(second.ruleKey().rule()).isEqualTo("Exported"); + assertThat(second.primaryLocation().message()).isEqualTo("exported type User should have comment or be unexported"); + assertThat(second.primaryLocation().textRange().start().line()).isEqualTo(2); + + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void no_issues_without_golint_property() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goLintReportSensor(), context); + assertThat(externalIssues).isEmpty(); + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void no_issues_with_invalid_report_path() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golint.reportPaths", REPORT_BASE_PATH.resolve("invalid-path.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goLintReportSensor(), context); + assertThat(externalIssues).isEmpty(); + List warnings = logTester.logs(Level.WARN); + assertThat(warnings) + .hasSize(1) + .hasSameSizeAs(analysisWarnings); + assertThat(warnings.get(0)) + .startsWith("Unable to import Golint report file(s):") + .contains("invalid-path.txt") + .endsWith("The report file(s) can not be found. Check that the property 'sonar.go.golint.reportPaths' is correctly configured."); + assertThat(analysisWarnings.get(0)) + .startsWith("Unable to import 1 Golint report file(s).") + .endsWith("Please check that property 'sonar.go.golint.reportPaths' is correctly configured and the analysis logs for more details."); + } + + @Test + void no_issues_with_invalid_report_line() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golint.reportPaths", REPORT_BASE_PATH.resolve("golint-report-with-error.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goLintReportSensor(), context); + assertThat(externalIssues).hasSize(1); + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + assertThat(logTester.logs(Level.DEBUG)).hasSize(1); + assertThat(logTester.logs(Level.DEBUG).get(0)).startsWith("GoLintReportSensor: Unexpected line: xyz"); + } + + @Test + void no_issues_with_invalid_report_file() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golint.reportPaths", REPORT_BASE_PATH.resolve("golint-report-with-wrong-file.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goLintReportSensor(), context); + assertThat(externalIssues).isEmpty(); + assertThat(logTester.logs(Level.WARN)) + .hasSize(1) + .contains("GoLintReportSensor: No input file found for foo.go. No Golint issues will be imported on this file."); + } + + @Test + void should_parse_golint_report_line() { + String line = "./vendor/github.com/foo/go-bar/hello_world.go:550:12: redundant or: n == 2 || n == 2"; + org.sonar.go.externalreport.ExternalIssue issue = goLintReportSensor().parse(line); + assertThat(issue).isNotNull(); + assertThat(issue.linter).isEqualTo("golint"); + assertThat(issue.type).isEqualTo(RuleType.CODE_SMELL); + assertThat(issue.ruleKey).isEqualTo(GENERIC_ISSUE_KEY); + assertThat(issue.filename).isEqualTo("./vendor/github.com/foo/go-bar/hello_world.go"); + assertThat(issue.lineNumber).isEqualTo(550); + assertThat(issue.message).isEqualTo("redundant or: n == 2 || n == 2"); + } + + @Test + void should_match_golint_all_keys() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golint.reportPaths", REPORT_BASE_PATH.resolve("all-golint-report.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goLintReportSensor(), context); + assertThat(externalIssues).hasSize(102); + + Stream uniqueKeys = externalIssues.stream().map(externalIssue -> externalIssue.ruleKey().rule()).distinct(); + assertThat(uniqueKeys).hasSize(18); + // all messages are associated to a rule key + assertThat(externalIssues).filteredOn(i -> i.ruleKey().rule().equals(GENERIC_ISSUE_KEY)).isEmpty(); + } + + @Test + void should_match_to_generic_issue_if_match_not_found() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golint.reportPaths", REPORT_BASE_PATH.resolve("golint-with-unknown-message.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goLintReportSensor(), context); + assertThat(externalIssues.get(0).ruleKey().rule()).isEqualTo(GENERIC_ISSUE_KEY); + } + + private GoLintReportSensor goLintReportSensor() { + return new GoLintReportSensor(analysisWarnings::add); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoMetaLinterReportSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoMetaLinterReportSensorTest.java new file mode 100644 index 00000000..aa8a347c --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoMetaLinterReportSensorTest.java @@ -0,0 +1,160 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.api.rules.RuleType; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.go.externalreport.AbstractReportSensor.GENERIC_ISSUE_KEY; +import static org.sonar.go.externalreport.ExternalLinterSensorHelper.REPORT_BASE_PATH; + +class GoMetaLinterReportSensorTest { + + private final List analysisWarnings = new ArrayList<>(); + + @BeforeEach + void setup() { + analysisWarnings.clear(); + } + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void test_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + goMetaLinterReportSensor().describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Import of GoMetaLinter issues"); + assertThat(sensorDescriptor.languages()).containsOnly("go"); + } + + @Test + void issues_with_sonarqube() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.gometalinter.reportPaths", REPORT_BASE_PATH.resolve("gometalinter-report.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goMetaLinterReportSensor(), context); + assertThat(externalIssues).hasSize(3); + + org.sonar.api.batch.sensor.issue.ExternalIssue first = externalIssues.get(0); + assertThat(first.type()).isEqualTo(RuleType.BUG); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + assertThat(first.ruleKey().repository()).isEqualTo("external_govet"); + assertThat(first.ruleKey().rule()).isEqualTo("assign"); + assertThat(first.primaryLocation().message()).isEqualTo("self-assignment of name to name"); + assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(1); + + org.sonar.api.batch.sensor.issue.ExternalIssue second = externalIssues.get(1); + assertThat(second.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(second.severity()).isEqualTo(Severity.MAJOR); + assertThat(second.ruleKey().repository()).isEqualTo("external_interfacer"); + assertThat(second.ruleKey().rule()).isEqualTo("issue"); + assertThat(second.primaryLocation().message()).isEqualTo("other (declaration) of ascii_allowed"); + assertThat(second.primaryLocation().textRange().start().line()).isEqualTo(2); + + org.sonar.api.batch.sensor.issue.ExternalIssue third = externalIssues.get(2); + assertThat(third.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(third.severity()).isEqualTo(Severity.MAJOR); + assertThat(third.ruleKey().repository()).isEqualTo("external_golint"); + assertThat(third.ruleKey().rule()).isEqualTo("Exported"); + assertThat(third.primaryLocation().message()).isEqualTo("exported type User should have comment or be unexported"); + assertThat(third.primaryLocation().textRange().start().line()).isEqualTo(3); + + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void no_issues_with_invalid_report_line() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.gometalinter.reportPaths", REPORT_BASE_PATH.resolve("gometalinter-report-with-error.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goMetaLinterReportSensor(), context); + assertThat(externalIssues).hasSize(1); + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + assertThat(logTester.logs(Level.DEBUG)).hasSize(1); + assertThat(logTester.logs(Level.DEBUG).get(0)).startsWith("GoMetaLinterReportSensor: Unexpected line: invalid-invalid-invalid"); + } + + @Test + void should_parse_gometalinter_report_warning_line() { + String line = "SelfAssignement.go:4:6:warning: exported type User should have comment or be unexported (golint)"; + org.sonar.go.externalreport.ExternalIssue issue = goMetaLinterReportSensor().parse(line); + assertThat(issue).isNotNull(); + assertThat(issue.linter).isEqualTo("golint"); + assertThat(issue.type).isEqualTo(RuleType.CODE_SMELL); + assertThat(issue.ruleKey).isEqualTo("Exported"); + assertThat(issue.filename).isEqualTo("SelfAssignement.go"); + assertThat(issue.lineNumber).isEqualTo(4); + assertThat(issue.message).isEqualTo("exported type User should have comment or be unexported"); + } + + @Test + void should_parse_gometalinter_report_error_line() { + String line = "duplication/pivot.go:14:5:error: ascii_allowed redeclared in this block (gotype)"; + org.sonar.go.externalreport.ExternalIssue issue = goMetaLinterReportSensor().parse(line); + assertThat(issue).isNotNull(); + assertThat(issue.linter).isEqualTo("gotype"); + assertThat(issue.type).isEqualTo(RuleType.BUG); + assertThat(issue.ruleKey).isEqualTo(GENERIC_ISSUE_KEY); + assertThat(issue.filename).isEqualTo("duplication/pivot.go"); + assertThat(issue.lineNumber).isEqualTo(14); + assertThat(issue.message).isEqualTo("ascii_allowed redeclared in this block"); + } + + @Test + void should_parse_gometalinter_report_error_line_with_rulekey() { + String line = "SelfAssignement.go:6:19:warning: func (*User).rename is unused (U1000) (megacheck)"; + org.sonar.go.externalreport.ExternalIssue issue = goMetaLinterReportSensor().parse(line); + assertThat(issue).isNotNull(); + assertThat(issue.linter).isEqualTo("megacheck"); + assertThat(issue.type).isEqualTo(RuleType.CODE_SMELL); + assertThat(issue.ruleKey).isEqualTo("U1000"); + assertThat(issue.filename).isEqualTo("SelfAssignement.go"); + assertThat(issue.lineNumber).isEqualTo(6); + assertThat(issue.message).isEqualTo("func (*User).rename is unused"); + } + + @Test + void should_parse_gometalinter_report_error_line_with_invalid_rulekey() { + String line = "SelfAssignement.go:6:19:warning: func (*User).rename is unused (Not a rule key) (megacheck)"; + org.sonar.go.externalreport.ExternalIssue issue = goMetaLinterReportSensor().parse(line); + assertThat(issue).isNotNull(); + assertThat(issue.linter).isEqualTo("megacheck"); + assertThat(issue.type).isEqualTo(RuleType.CODE_SMELL); + assertThat(issue.ruleKey).isEqualTo(GENERIC_ISSUE_KEY); + assertThat(issue.filename).isEqualTo("SelfAssignement.go"); + assertThat(issue.lineNumber).isEqualTo(6); + assertThat(issue.message).isEqualTo("func (*User).rename is unused (Not a rule key)"); + } + + private GoMetaLinterReportSensor goMetaLinterReportSensor() { + return new GoMetaLinterReportSensor(analysisWarnings::add); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoVetReportSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoVetReportSensorTest.java new file mode 100644 index 00000000..9013eea6 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GoVetReportSensorTest.java @@ -0,0 +1,174 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.api.rules.RuleType; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.go.externalreport.AbstractReportSensor.GENERIC_ISSUE_KEY; +import static org.sonar.go.externalreport.ExternalLinterSensorHelper.REPORT_BASE_PATH; + +class GoVetReportSensorTest { + + private final List analysisWarnings = new ArrayList<>(); + + @BeforeEach + void setup() { + analysisWarnings.clear(); + } + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void test_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + goVetReportSensor().describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Import of go vet issues"); + assertThat(sensorDescriptor.languages()).containsOnly("go"); + } + + @Test + void issues_with_sonarqube() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.govet.reportPaths", REPORT_BASE_PATH.resolve("govet-report.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goVetReportSensor(), context); + assertThat(externalIssues).hasSize(3); + + ExternalIssue first = externalIssues.get(0); + assertThat(first.ruleKey().rule()).isEqualTo("nilfunc"); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + assertThat(first.primaryLocation().message()).isEqualTo("comparison of function Foo == nil is always false"); + assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(1); + + ExternalIssue second = externalIssues.get(1); + assertThat(second.ruleKey().rule()).isEqualTo("printf"); + assertThat(second.severity()).isEqualTo(Severity.MAJOR); + assertThat(second.primaryLocation().message()).isEqualTo("Printf format %s has arg &str of wrong type *string"); + assertThat(second.primaryLocation().textRange().start().line()).isEqualTo(2); + + ExternalIssue third = externalIssues.get(2); + assertThat(third.ruleKey().rule()).isEqualTo("unreachable"); + assertThat(third.severity()).isEqualTo(Severity.MAJOR); + assertThat(third.primaryLocation().message()).isEqualTo("unreachable code"); + assertThat(third.primaryLocation().textRange().start().line()).isEqualTo(2); + + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void no_issues_without_govet_property() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goVetReportSensor(), context); + assertThat(externalIssues).isEmpty(); + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void no_issues_with_invalid_report_path() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.govet.reportPaths", REPORT_BASE_PATH.resolve("invalid-path.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goVetReportSensor(), context); + assertThat(externalIssues).isEmpty(); + List warnings = logTester.logs(Level.WARN); + assertThat(warnings) + .hasSize(1) + .hasSameSizeAs(analysisWarnings); + assertThat(warnings.get(0)) + .startsWith("Unable to import go vet report file(s):") + .contains("invalid-path.txt") + .endsWith("The report file(s) can not be found. Check that the property 'sonar.go.govet.reportPaths' is correctly configured."); + assertThat(analysisWarnings.get(0)) + .startsWith("Unable to import 1 go vet report file(s).") + .endsWith("Please check that property 'sonar.go.govet.reportPaths' is correctly configured and the analysis logs for more details."); + } + + @Test + void no_issues_with_invalid_report_line() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.govet.reportPaths", REPORT_BASE_PATH.resolve("govet-report-with-error.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goVetReportSensor(), context); + assertThat(externalIssues).hasSize(1); + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + assertThat(logTester.logs(Level.DEBUG)).hasSize(1); + assertThat(logTester.logs(Level.DEBUG).get(0)).startsWith("GoVetReportSensor: Unexpected line: abcdefghijkl"); + } + + @Test + void should_parse_govet_report_line() { + String line = "./vendor/github.com/foo/go-bar/hello_world.go:550: redundant or: n == 2 || n == 2"; + org.sonar.go.externalreport.ExternalIssue issue = goVetReportSensor().parse(line); + assertThat(issue).isNotNull(); + assertThat(issue.linter).isEqualTo("govet"); + assertThat(issue.type).isEqualTo(RuleType.BUG); + assertThat(issue.ruleKey).isEqualTo("bool"); + assertThat(issue.filename).isEqualTo("./vendor/github.com/foo/go-bar/hello_world.go"); + assertThat(issue.lineNumber).isEqualTo(550); + assertThat(issue.message).isEqualTo("redundant or: n == 2 || n == 2"); + } + + @Test + void should_match_govet_all_keys() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.govet.reportPaths", REPORT_BASE_PATH.resolve("all-govet-report.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goVetReportSensor(), context); + assertThat(externalIssues).hasSize(263); + + Stream uniqueKeys = externalIssues.stream().map(externalIssue -> externalIssue.ruleKey().rule()).distinct(); + assertThat(uniqueKeys).hasSize(19); + // all messages are associated to a rule key + assertThat(externalIssues).filteredOn(i -> i.ruleKey().rule().equals(GENERIC_ISSUE_KEY)).isEmpty(); + } + + @Test + void should_match_govet_asm_keys() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.govet.reportPaths", REPORT_BASE_PATH.resolve("asm-govet-report.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goVetReportSensor(), context); + assertThat(externalIssues).hasSize(734); + // all messages should be matched to asmdecl rule key + assertThat(externalIssues).extracting(i -> i.ruleKey().rule()).containsOnly("asmdecl"); + } + + @Test + void should_match_to_generic_issue_if_match_not_found() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.govet.reportPaths", REPORT_BASE_PATH.resolve("govet-with-unknown-message.txt").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(goVetReportSensor(), context); + assertThat(externalIssues.get(0).ruleKey().rule()).isEqualTo(GENERIC_ISSUE_KEY); + } + + private GoVetReportSensor goVetReportSensor() { + return new GoVetReportSensor(analysisWarnings::add); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GolangCILintReportSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GolangCILintReportSensorTest.java new file mode 100644 index 00000000..df4760c7 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/externalreport/GolangCILintReportSensorTest.java @@ -0,0 +1,107 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.externalreport; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.rule.Severity; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.ExternalIssue; +import org.sonar.api.rules.RuleType; +import org.sonarsource.slang.testing.ThreadLocalLogTester; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.go.externalreport.ExternalLinterSensorHelper.REPORT_BASE_PATH; + +class GolangCILintReportSensorTest { + + private final List analysisWarnings = new ArrayList<>(); + + @BeforeEach + void setup() { + analysisWarnings.clear(); + } + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @Test + void test_descriptor() { + DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor(); + golangCILintReportSensor().describe(sensorDescriptor); + assertThat(sensorDescriptor.name()).isEqualTo("Import of GolangCI-Lint issues"); + assertThat(sensorDescriptor.languages()).containsOnly("go"); + } + + private GolangCILintReportSensor golangCILintReportSensor() { + return new GolangCILintReportSensor(analysisWarnings::add); + } + + @Test + void issues_with_sonarqube() throws IOException { + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golangci-lint.reportPaths", REPORT_BASE_PATH.resolve("golandci-lint-report.xml").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(golangCILintReportSensor(), context); + assertThat(externalIssues).hasSize(2); + + org.sonar.api.batch.sensor.issue.ExternalIssue first = externalIssues.get(0); + assertThat(first.type()).isEqualTo(RuleType.BUG); + assertThat(first.severity()).isEqualTo(Severity.MAJOR); + assertThat(first.ruleKey().repository()).isEqualTo("external_golangci-lint"); + assertThat(first.ruleKey().rule()).isEqualTo("deadcode.bug.major"); + assertThat(first.primaryLocation().message()).isEqualTo("`three` is unused"); + assertThat(first.primaryLocation().textRange().start().line()).isEqualTo(3); + + ExternalIssue second = externalIssues.get(1); + assertThat(second.type()).isEqualTo(RuleType.VULNERABILITY); + assertThat(second.severity()).isEqualTo(Severity.MAJOR); + assertThat(second.ruleKey().repository()).isEqualTo("external_golangci-lint"); + assertThat(second.ruleKey().rule()).isEqualTo("gosec"); + assertThat(second.primaryLocation().message()).isEqualTo("G402: TLS InsecureSkipVerify set true."); + assertThat(second.primaryLocation().inputComponent().key()).isEqualTo("module:main.go"); + assertThat(second.primaryLocation().textRange().start().line()).isEqualTo(4); + + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + + @Test + void import_check_style_report_same_source_different_key() throws IOException { + // Check that rules have different key based on the severity + SensorContextTester context = ExternalLinterSensorHelper.createContext(); + context.settings().setProperty("sonar.go.golangci-lint.reportPaths", REPORT_BASE_PATH.resolve("checkstyle-different-severity.xml").toString()); + List externalIssues = ExternalLinterSensorHelper.executeSensor(golangCILintReportSensor(), context); + assertThat(externalIssues).hasSize(6); + + assertThat(externalIssues.get(0).ruleKey().rule()).isEqualTo("source1.bug.major"); + assertThat(externalIssues.get(1).ruleKey().rule()).isEqualTo("source1.code_smell.minor"); + assertThat(externalIssues.get(2).ruleKey().rule()).isEqualTo("source1.code_smell.major"); + assertThat(externalIssues.get(3).ruleKey().rule()).isEqualTo("source1.code_smell.major"); + assertThat(externalIssues.get(4).ruleKey().rule()).isEqualTo("source2.bug.major"); + assertThat(externalIssues.get(5).ruleKey().rule()).isEqualTo("source2.code_smell.major"); + } + +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoCheckListTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoCheckListTest.java new file mode 100644 index 00000000..9386b512 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoCheckListTest.java @@ -0,0 +1,56 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import java.util.List; +import java.util.stream.Collectors; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.sonarsource.slang.testing.PackageScanner; + +import static org.assertj.core.api.Java6Assertions.assertThat; + +class GoCheckListTest { + + private static final String GO_CHECKS_PACKAGE = "org.sonar.go.checks"; + + @Test + void go_checks_size() { + Assertions.assertThat(GoCheckList.checks()).hasSize(38); + } + + @Test + void go_specific_checks_are_added_to_check_list() { + List checkListNames = GoCheckList.checks().stream().map(Class::getName).collect(Collectors.toList()); + List languageImplementation = PackageScanner.findSlangChecksInPackage(GO_CHECKS_PACKAGE); + for (String languageCheck : languageImplementation) { + assertThat(checkListNames).contains(languageCheck); + assertThat(languageCheck).endsWith("GoCheck"); + } + } + + @Test + void go_excluded_not_present() { + List> checks = GoCheckList.checks(); + for (Class excluded : GoCheckList.GO_CHECK_BLACK_LIST) { + assertThat(checks).doesNotContain(excluded); + } + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoExclusionsFileFilterTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoExclusionsFileFilterTest.java new file mode 100644 index 00000000..11cbad24 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoExclusionsFileFilterTest.java @@ -0,0 +1,92 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.junit.jupiter.api.Test; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.config.internal.MapSettings; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class GoExclusionsFileFilterTest { + + @Test + void should_exclude_vendor_dir() throws Exception { + MapSettings settings = new MapSettings(); + settings.setProperty(GoPlugin.EXCLUSIONS_KEY, GoPlugin.EXCLUSIONS_DEFAULT_VALUE); + GoExclusionsFileFilter filter = new GoExclusionsFileFilter(settings.asConfig()); + assertTrue(filter.accept(inputFile("file.go"))); + assertFalse(filter.accept(inputFile("vendor/file.go"))); + assertFalse(filter.accept(inputFile("vendor/someDir/file.go"))); + assertFalse(filter.accept(inputFile("someDir/vendor/file.go"))); + } + + @Test + void should_exclude_only_go() throws Exception { + MapSettings settings = new MapSettings(); + settings.setProperty(GoPlugin.EXCLUSIONS_KEY, GoPlugin.EXCLUSIONS_DEFAULT_VALUE); + GoExclusionsFileFilter filter = new GoExclusionsFileFilter(settings.asConfig()); + assertFalse(filter.accept(inputFile("vendor/file.go"))); + assertTrue(filter.accept(inputFile("vendor/file.json"))); + } + + @Test + void should_include_vendor_when_property_is_overridden() throws Exception { + MapSettings settings = new MapSettings(); + + settings.setProperty(GoPlugin.EXCLUSIONS_KEY, ""); + GoExclusionsFileFilter filter = new GoExclusionsFileFilter(settings.asConfig()); + + assertTrue(filter.accept(inputFile("file.go"))); + assertTrue(filter.accept(inputFile("vendor/file.go"))); + assertTrue(filter.accept(inputFile("vendor/someDir/file.go"))); + assertTrue(filter.accept(inputFile("someDir/vendor/file.go"))); + } + + @Test + void should_exclude_using_custom_path_regex() throws Exception { + MapSettings settings = new MapSettings(); + + settings.setProperty(GoPlugin.EXCLUSIONS_KEY, "**/lib/**"); + GoExclusionsFileFilter filter = new GoExclusionsFileFilter(settings.asConfig()); + + assertTrue(filter.accept(inputFile("file.go"))); + assertTrue(filter.accept(inputFile("vendor/file.go"))); + assertFalse(filter.accept(inputFile("lib/file.go"))); + assertFalse(filter.accept(inputFile("someDir/lib/file.go"))); + } + + @Test + void should_ignore_empty_path_regex() throws Exception { + MapSettings settings = new MapSettings(); + settings.setProperty(GoPlugin.EXCLUSIONS_KEY, "," + GoPlugin.EXCLUSIONS_DEFAULT_VALUE + ","); + GoExclusionsFileFilter filter = new GoExclusionsFileFilter(settings.asConfig()); + + assertTrue(filter.accept(inputFile("file.go"))); + assertFalse(filter.accept(inputFile("vendor/file.go"))); + } + + private DefaultInputFile inputFile(String file) { + return new TestInputFileBuilder("test","test_vendor/" + file).setLanguage(file.split("\\.")[1]).build(); + } + +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoLanguageTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoLanguageTest.java new file mode 100644 index 00000000..fd5794d6 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoLanguageTest.java @@ -0,0 +1,43 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.junit.jupiter.api.Test; +import org.sonar.api.config.internal.MapSettings; + +import static org.assertj.core.api.Assertions.assertThat; + +class GoLanguageTest { + + @Test + void should_have_correct_file_extensions() { + MapSettings mapSettings = new MapSettings(); + GoLanguage typeScriptLanguage = new GoLanguage(mapSettings.asConfig()); + assertThat(typeScriptLanguage.getFileSuffixes()).containsExactly(".go"); + } + + @Test + void can_override_file_extensions() { + MapSettings mapSettings = new MapSettings(); + mapSettings.setProperty("sonar.go.file.suffixes", ".go1,.go2"); + GoLanguage typeScriptLanguage = new GoLanguage(mapSettings.asConfig()); + assertThat(typeScriptLanguage.getFileSuffixes()).containsExactly(".go1",".go2"); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoPluginTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoPluginTest.java new file mode 100644 index 00000000..fdb9965d --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoPluginTest.java @@ -0,0 +1,53 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.junit.jupiter.api.Test; +import org.sonar.api.Plugin; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.internal.PluginContextImpl; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.utils.Version; + +import static org.assertj.core.api.Assertions.assertThat; + +class GoPluginTest { + + @Test + void count_extension_points_7_9() { + SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(7, 9), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); + Plugin.Context context = new Plugin.Context(runtime); + Plugin underTest = new GoPlugin(); + underTest.define(context); + assertThat(context.getExtensions()).hasSize(20); + } + + @Test + void test_sonarlint() { + SonarRuntime runtime = SonarRuntimeImpl.forSonarLint(Version.create(3, 9)); + Plugin.Context context = new PluginContextImpl.Builder().setSonarRuntime(runtime).build(); + + Plugin underTest = new GoPlugin(); + underTest.define(context); + assertThat(context.getExtensions()).hasSize(8); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoProfileDefinitionTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoProfileDefinitionTest.java new file mode 100644 index 00000000..b17975ee --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoProfileDefinitionTest.java @@ -0,0 +1,46 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import org.junit.jupiter.api.Test; +import org.sonar.api.server.profile.BuiltInQualityProfilesDefinition; +import org.sonar.api.utils.ValidationMessages; + +import static org.assertj.core.api.Assertions.assertThat; + +class GoProfileDefinitionTest { + + @Test + void should_create_sonar_way_profile() { + ValidationMessages validation = ValidationMessages.create(); + + BuiltInQualityProfilesDefinition.Context context = new BuiltInQualityProfilesDefinition.Context(); + new GoProfileDefinition().define(context); + + assertThat(context.profilesByLanguageAndName()).hasSize(1); + BuiltInQualityProfilesDefinition.BuiltInQualityProfile profile = context.profile("go", "Sonar way"); + + assertThat(profile.language()).isEqualTo("go"); + assertThat(profile.name()).isEqualTo("Sonar way"); + assertThat(profile.rules()).hasSize(25); + assertThat(validation.hasErrors()).isFalse(); + } + +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoRulesDefinitionTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoRulesDefinitionTest.java new file mode 100644 index 00000000..2fa1c062 --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoRulesDefinitionTest.java @@ -0,0 +1,128 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import java.util.List; +import java.util.stream.Collectors; +import org.junit.jupiter.api.Test; +import org.sonar.api.SonarEdition; +import org.sonar.api.SonarQubeSide; +import org.sonar.api.SonarRuntime; +import org.sonar.api.internal.SonarRuntimeImpl; +import org.sonar.api.rules.RuleType; +import org.sonar.api.server.debt.DebtRemediationFunction; +import org.sonar.api.server.rule.RulesDefinition; +import org.sonar.api.utils.Version; +import org.sonar.go.externalreport.ExternalKeyUtils; + +import static org.assertj.core.api.Assertions.assertThat; + +class GoRulesDefinitionTest { + + private static final SonarRuntime RUNTIME = SonarRuntimeImpl.forSonarQube(Version.create(8, 9), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY); + + @Test + void test() { + GoRulesDefinition rulesDefinition = new GoRulesDefinition(RUNTIME); + RulesDefinition.Context context = new RulesDefinition.Context(); + rulesDefinition.define(context); + + assertThat(context.repositories()).hasSize(3); + + RulesDefinition.Repository goRepository = context.repository("go"); + + assertThat(goRepository.name()).isEqualTo("Sonar"); + assertThat(goRepository.language()).isEqualTo("go"); + assertThat(goRepository.rules()).hasSize(GoCheckList.checks().size()); + + RulesDefinition.Rule rule = goRepository.rule("S4663"); + assertThat(rule).isNotNull(); + assertThat(rule.name()).isEqualTo("Multi-line comments should not be empty"); + assertThat(rule.debtRemediationFunction().type()).isEqualTo(DebtRemediationFunction.Type.CONSTANT_ISSUE); + assertThat(rule.type()).isEqualTo(RuleType.CODE_SMELL); + assertThat(rule.activatedByDefault()).isTrue(); + } + + @Test + void test_external_repositories() { + GoRulesDefinition rulesDefinition = new GoRulesDefinition(RUNTIME); + RulesDefinition.Context context = new RulesDefinition.Context(); + rulesDefinition.define(context); + RulesDefinition.Repository golintRepository = context.repository("external_golint"); + RulesDefinition.Repository govetRepository = context.repository("external_govet"); + + assertThat(context.repositories()).hasSize(3); + + assertThat(golintRepository.name()).isEqualTo("Golint"); + assertThat(govetRepository.name()).isEqualTo("go vet"); + + assertThat(golintRepository.language()).isEqualTo("go"); + assertThat(govetRepository.language()).isEqualTo("go"); + + assertThat(golintRepository.isExternal()).isTrue(); + assertThat(govetRepository.isExternal()).isTrue(); + + assertThat(golintRepository.rules()).hasSize(18); + assertThat(ExternalKeyUtils.GO_LINT_KEYS).hasSize(18); + + assertThat(govetRepository.rules()).hasSize(21); + assertThat(ExternalKeyUtils.GO_VET_KEYS).hasSize(21); + + List govetKeysWithoutDefinition = ExternalKeyUtils.GO_VET_KEYS.stream() + .map(x -> x.key) + .filter(key -> govetRepository.rule(key) == null) + .collect(Collectors.toList()); + assertThat(govetKeysWithoutDefinition).isEmpty(); + + List golintKeysWithoutDefinition = ExternalKeyUtils.GO_LINT_KEYS.stream() + .map(x -> x.key) + .filter(key -> golintRepository.rule(key) == null) + .collect(Collectors.toList()); + assertThat(golintKeysWithoutDefinition).isEmpty(); + } + + @Test + void owasp_security_standard_includes_2021() { + RulesDefinition.Repository repository = getRepositoryForVersion(Version.create(9, 3)); + + RulesDefinition.Rule rule = repository.rule("S1313"); + assertThat(rule).isNotNull(); + assertThat(rule.securityStandards()).containsExactlyInAnyOrder("owaspTop10:a3", "owaspTop10-2021:a1"); + } + + @Test + void owasp_security_standard() { + RulesDefinition.Repository repository = getRepositoryForVersion(Version.create(8, 9)); + + RulesDefinition.Rule rule = repository.rule("S1313"); + assertThat(rule).isNotNull(); + assertThat(rule.securityStandards()).containsExactly("owaspTop10:a3"); + } + + private RulesDefinition.Repository getRepositoryForVersion(Version version) { + GoRulesDefinition rulesDefinition = new GoRulesDefinition( + SonarRuntimeImpl.forSonarQube(version, SonarQubeSide.SCANNER, SonarEdition.COMMUNITY)); + RulesDefinition.Context context = new RulesDefinition.Context(); + rulesDefinition.define(context); + + return context.repository("go"); + } + +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoSensorTest.java new file mode 100644 index 00000000..2f20629d --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/GoSensorTest.java @@ -0,0 +1,408 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import javax.annotation.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.rule.ActiveRules; +import org.sonar.api.batch.rule.CheckFactory; +import org.sonar.api.batch.rule.Checks; +import org.sonar.api.batch.rule.internal.ActiveRulesBuilder; +import org.sonar.api.batch.rule.internal.NewActiveRule; +import org.sonar.api.batch.sensor.highlighting.TypeOfText; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.batch.sensor.issue.internal.DefaultNoSonarFilter; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.measures.CoreMetrics; +import org.sonar.api.measures.FileLinesContext; +import org.sonar.api.measures.FileLinesContextFactory; +import org.sonar.api.rule.RuleKey; +import org.sonarsource.slang.testing.ThreadLocalLogTester; +import org.sonar.go.converter.GoConverter; +import org.sonarsource.slang.checks.api.SlangCheck; +import org.sonarsource.slang.testing.AbstractSensorTest; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.when; + +class GoSensorTest { + + private Path workDir; + private GoConverter singleInstanceGoConverter; + private Path projectDir; + private SensorContextTester sensorContext; + private FileLinesContextFactory fileLinesContextFactory = mock(FileLinesContextFactory.class); + private FileLinesContextTester fileLinesContext; + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + @BeforeEach + void setUp() throws IOException { + workDir = Files.createTempDirectory("gotest"); + workDir.toFile().deleteOnExit(); + singleInstanceGoConverter = new GoConverter(workDir.toFile()); + projectDir = Files.createTempDirectory("gotestProject"); + projectDir.toFile().deleteOnExit(); + sensorContext = SensorContextTester.create(workDir); + sensorContext.fileSystem().setWorkDir(workDir); + sensorContext.settings().setProperty("sonar.slang.converter.validation", "throw"); + fileLinesContext = new FileLinesContextTester(); + when(fileLinesContextFactory.createFor(any(InputFile.class))).thenReturn(fileLinesContext); + } + + @Test + void test_description() { + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); + + getSensor("S2068").describe(descriptor); + assertThat(descriptor.name()).isEqualTo("Code Quality and Security for Go"); + assertThat(descriptor.languages()).containsOnly("go"); + } + + @Test + void test_issue() throws IOException { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.MAIN, + "package main \n" + + "\n" + + "func test() {\n" + + " pwd := \"secret\"\n" + + "}"); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor("S2068"); + goSensor.execute(sensorContext); + assertThat(sensorContext.allIssues()).hasSize(1); + } + + @Test + void test_file_issue() throws IOException { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.MAIN, + "// TODO implement the logic \n package main \n"); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor("S1135"); + goSensor.execute(sensorContext); + assertThat(sensorContext.allIssues()).hasSize(1); + } + + @Test + void test_line_issue() throws IOException { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.MAIN, + "package main\n"); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor("S103"); + goSensor.execute(sensorContext); + assertThat(sensorContext.allIssues()).hasSize(1); + } + + @Test + void test_failure() throws Exception { + InputFile failingFile = createInputFile("lets.go", InputFile.Type.MAIN, + "package main \n" + + "\n" + + "func test() {\n" + + " pwd := \"secret\"\n" + + "}"); + failingFile = spy(failingFile); + doThrow(new IOException("The file is corrupted")).when(failingFile).contents(); + + sensorContext.fileSystem().add(failingFile); + GoSensor goSensor = getSensor("S1135"); + goSensor.execute(sensorContext); + assertThat(logTester.logs(Level.ERROR)).contains("Cannot read 'lets.go': The file is corrupted"); + } + + @Test + void test_empty_file() throws Exception { + InputFile failingFile = createInputFile("lets.go", InputFile.Type.MAIN, ""); + sensorContext.fileSystem().add(failingFile); + GoSensor goSensor = getSensor("S1135"); + goSensor.execute(sensorContext); + assertThat(logTester.logs(Level.ERROR)).isEmpty(); + } + + @Test + void metrics() { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.MAIN, + /* 01 */"// This is not a line of code\n" + + /* 02 */"package main\n" + + /* 03 */"import \"fmt\"\n" + + /* 04 */"type class1 struct { x, y int }\n" + + /* 05 */"type class2 struct { a, b string }\n" + + /* 06 */"type anyObject interface {}\n" + + /* 07 */"func fun1() {\n" + + /* 08 */" fmt.Println(\"Statement 1\")\n" + + /* 09 */"}\n" + + /* 10 */"func fun2(i int) {\n" + + /* 11 */" switch i { // Statement 2\n" + + /* 12 */" case 2:\n" + + /* 13 */" fmt.Println(\n" + + /* 14 */" \"Not a Statement 3\",\n" + + /* 15 */" )\n" + + /* 16 */" }\n" + + /* 17 */"}\n" + + /* 18 */"func fun3(x interface{}) int {\n" + + /* 19 */" return 42 // Statement 4\n" + + /* 20 */"}\n"); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor(); + goSensor.execute(sensorContext); + assertThat(sensorContext.allIssues()).isEmpty(); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(19); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.COMMENT_LINES).value()).isEqualTo(3); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.FUNCTIONS).value()).isEqualTo(3); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.CLASSES).value()).isEqualTo(3); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.STATEMENTS).value()).isEqualTo(4); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.COGNITIVE_COMPLEXITY).value()).isEqualTo(1); + + assertThat(fileLinesContext.saveCount).isEqualTo(1); + + assertThat(fileLinesContext.metrics.keySet()).containsExactlyInAnyOrder(CoreMetrics.NCLOC_DATA_KEY, + CoreMetrics.EXECUTABLE_LINES_DATA_KEY); + + + assertThat(fileLinesContext.metrics.get(CoreMetrics.NCLOC_DATA_KEY)).containsExactlyInAnyOrder( + "2:1", "3:1", "4:1", "5:1", "6:1", "7:1", "8:1", "9:1", "10:1", "11:1", + "12:1", "13:1", "14:1", "15:1", "16:1", "17:1", "18:1", "19:1", "20:1"); + + assertThat(fileLinesContext.metrics.get(CoreMetrics.EXECUTABLE_LINES_DATA_KEY)).containsExactlyInAnyOrder( + "8:1", "11:1", "13:1", "19:1"); + } + + @Test + void test_not_executable_lines() { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.MAIN, + "package awesomeProject\n" + + "const a = \"a\"\n" + + "var myVar int = 12\n" + + "var i, j int = 1, 2\n" + + "var c, c1, c2, c3, c4 int\n" + + "const (\n" + + "\tUpdate = \"update\"\n" + + "\tDelete = \"delete\"\n" + + ")\n" + + "type Message struct {\n" + + "}\n" + + "type (\n" + + "\tRrsType string\n" + + ")\n" + ); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor(); + goSensor.execute(sensorContext); + + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.NCLOC).value()).isEqualTo(14); + assertThat(fileLinesContext.metrics.get(CoreMetrics.EXECUTABLE_LINES_DATA_KEY)).isNull(); + } + + @Test + void metrics_for_test_file() { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.TEST, + "// This is not a line of code\n" + + "package main\n" + + "import \"fmt\"\n" + + "func main() {\n" + + " fmt.Println(\"Hello\")\n" + + "}\n"); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor(); + goSensor.execute(sensorContext); + assertThat(sensorContext.allIssues()).isEmpty(); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.NCLOC)).isNull(); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.COMMENT_LINES)).isNull(); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.CLASSES)).isNull(); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.FUNCTIONS)).isNull(); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.STATEMENTS)).isNull(); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.COGNITIVE_COMPLEXITY)).isNull(); + + assertThat(fileLinesContext.saveCount).isZero(); + assertThat(fileLinesContext.metrics.keySet()).isEmpty(); + } + + @Test + void cognitive_complexity_metric() { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.MAIN, + "package main\n" + + "import \"fmt\"\n" + + "func fun1(i int) int {\n" + + " if i < 0 { // +1\n" + + " i++\n" + + " }\n" + + " return i\n" + + "}\n" + + "func fun2(i int) int {\n" + + " if i < 0 { // +1\n" + + " i--\n" + + " }\n" + + " f := func(int) int {\n" + + " if i < 0 { // +2 (incl 1 for nesting)\n" + + " i++\n" + + " }\n" + + " return i\n" + + " }\n" + + " return i + f(i)\n" + + "}\n" + + "\n"); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor(); + goSensor.execute(sensorContext); + assertThat(sensorContext.measure(inputFile.key(), CoreMetrics.COGNITIVE_COMPLEXITY).value()).isEqualTo(4); + } + + @Test + void always_use_the_same_ast_converter() { + GoSensor goSensor = getSensor(); + assertThat(goSensor.astConverter(sensorContext)).isSameAs(singleInstanceGoConverter); + assertThat(goSensor.astConverter(sensorContext)).isSameAs(singleInstanceGoConverter); + } + + @Test + void highlighting() throws Exception { + InputFile inputFile = createInputFile("lets.go", InputFile.Type.MAIN, + "//abc\n" + + "/*x*/\n" + + "package main\n" + + "import \"fmt\"\n" + + "type class1 struct { }\n" + + "func fun2(x string) int {\n" + + " return 42\n" + + "}\n"); + sensorContext.fileSystem().add(inputFile); + GoSensor goSensor = getSensor(); + goSensor.execute(sensorContext); + + String componentKey = "module:lets.go"; + // //abc + assertHighlighting(componentKey, 1, 1, 5, TypeOfText.COMMENT); + // /*x*/ + assertHighlighting(componentKey, 2, 1, 5, TypeOfText.COMMENT); + // package main + assertHighlighting(componentKey, 3, 1, 7, TypeOfText.KEYWORD); + assertHighlighting(componentKey, 3, 9, 12, null); + // import "fmt" + assertHighlighting(componentKey, 4, 1, 6, TypeOfText.KEYWORD); + assertHighlighting(componentKey, 4, 8, 12, TypeOfText.STRING); + // type class1 struct { } + assertHighlighting(componentKey, 5, 1, 4, TypeOfText.KEYWORD); + assertHighlighting(componentKey, 5, 6, 11, null); + assertHighlighting(componentKey, 5, 13, 18, TypeOfText.KEYWORD); + assertHighlighting(componentKey, 5, 20, 22, null); + // func fun2(x string) int { + assertHighlighting(componentKey, 6, 1, 4, TypeOfText.KEYWORD); + assertHighlighting(componentKey, 6, 6, 9, null); + assertHighlighting(componentKey, 6, 6, 12, null); + assertHighlighting(componentKey, 6, 13, 18, null); + assertHighlighting(componentKey, 6, 19, 20, null); + assertHighlighting(componentKey, 6, 21, 23, null); + // return 42 + assertHighlighting(componentKey, 7, 3, 8, TypeOfText.KEYWORD); + assertHighlighting(componentKey, 7, 10, 11, TypeOfText.CONSTANT); + } + + @Test + void repository_key() { + assertThat(getSensor().repositoryKey()).isEqualTo("go"); + } + + private void assertHighlighting(String componentKey, int line, int columnFirst, int columnLast, @Nullable TypeOfText type) { + for (int column = columnFirst; column <= columnLast; column++) { + List typeOfTexts = sensorContext.highlightingTypeAt(componentKey, line, column - 1); + if (type != null) { + assertThat(typeOfTexts).as("Expect highlighting " + type + " at line " + line + " lineOffset " + column).containsExactly(type); + } else { + assertThat(typeOfTexts).as("Expect no highlighting at line " + line + " lineOffset " + column).containsExactly(); + } + } + } + + private GoSensor getSensor(String... activeRuleArray) { + Set activeRuleSet = new HashSet<>(Arrays.asList(activeRuleArray)); + List> ruleClasses = GoCheckList.checks(); + List allKeys = ruleClasses.stream().map(ruleClass -> ((org.sonar.check.Rule) ruleClass.getAnnotations()[0]).key()).collect(Collectors.toList()); + ActiveRulesBuilder rulesBuilder = new ActiveRulesBuilder(); + allKeys.forEach(key -> { + if (activeRuleSet.contains(key)) { + NewActiveRule.Builder newActiveRuleBuilder = new NewActiveRule.Builder() + .setRuleKey(RuleKey.of(GoRulesDefinition.REPOSITORY_KEY, key)); + if (key.equals("S1451")) { + newActiveRuleBuilder.setParam("headerFormat", "some header format"); + } + rulesBuilder.addRule(newActiveRuleBuilder.build()); + } + }); + ActiveRules activeRules = rulesBuilder.build(); + CheckFactory checkFactory = new CheckFactory(activeRules); + Checks checks = checkFactory.create(GoRulesDefinition.REPOSITORY_KEY); + checks.addAnnotatedChecks((Iterable) ruleClasses); + return new GoSensor(AbstractSensorTest.SQ_LTS_RUNTIME, checkFactory, fileLinesContextFactory, new DefaultNoSonarFilter(), + new GoLanguage(new MapSettings().asConfig()), singleInstanceGoConverter); + } + + private InputFile createInputFile(String filename, InputFile.Type type, String content) { + Path filePath = projectDir.resolve(filename); + return TestInputFileBuilder.create("module", projectDir.toFile(), filePath.toFile()) + .setCharset(UTF_8) + .setLanguage(GoLanguage.KEY) + .setContents(content) + .setType(type) + .build(); + } + + private static class FileLinesContextTester implements FileLinesContext { + int saveCount = 0; + Map> metrics = new HashMap<>(); + + @Override + public void setIntValue(String metricKey, int line, int value) { + setStringValue(metricKey, line, String.valueOf(value)); + } + + @Override + public void setStringValue(String metricKey, int line, String value) { + metrics.computeIfAbsent(metricKey, key -> new HashSet<>()) + .add(line + ":" + value); + } + + @Override + public void save() { + saveCount++; + } + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/plugin/InstanceScopeGoConverterTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/InstanceScopeGoConverterTest.java new file mode 100644 index 00000000..9961873a --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/plugin/InstanceScopeGoConverterTest.java @@ -0,0 +1,43 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.plugin; + +import java.io.File; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.sonar.api.utils.TempFolder; +import org.sonarsource.slang.api.TopLevelTree; +import org.sonarsource.slang.api.Tree; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +class InstanceScopeGoConverterTest { + @Test + void constructor(@TempDir File tempDir) { + TempFolder tempFolder = mock(TempFolder.class); + when(tempFolder.newDir()).thenReturn(tempDir); + InstanceScopeGoConverter converter = new InstanceScopeGoConverter(tempFolder); + + Tree tree = converter.parse("package main\nfunc foo() {}"); + assertThat(tree).isInstanceOf(TopLevelTree.class); + } +} diff --git a/sonar-go-plugin/src/test/java/org/sonar/go/testreport/GoTestSensorTest.java b/sonar-go-plugin/src/test/java/org/sonar/go/testreport/GoTestSensorTest.java new file mode 100644 index 00000000..922aadec --- /dev/null +++ b/sonar-go-plugin/src/test/java/org/sonar/go/testreport/GoTestSensorTest.java @@ -0,0 +1,219 @@ +/* + * SonarSource Go + * Copyright (C) 2018-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.go.testreport; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.event.Level; +import org.sonar.api.batch.fs.InputFile; +import org.sonar.api.batch.fs.InputFile.Type; +import org.sonar.api.batch.fs.internal.DefaultFileSystem; +import org.sonar.api.batch.fs.internal.DefaultInputFile; +import org.sonar.api.batch.fs.internal.TestInputFileBuilder; +import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; +import org.sonar.api.batch.sensor.internal.SensorContextTester; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.measures.CoreMetrics; +import org.sonarsource.slang.testing.ThreadLocalLogTester; +import org.sonar.go.coverage.GoPathContext; +import org.sonar.go.testreport.GoTestSensor.TestInfo; + +import static org.assertj.core.api.Assertions.assertThat; + +class GoTestSensorTest { + + @RegisterExtension + public ThreadLocalLogTester logTester = new ThreadLocalLogTester(); + + private final Path goPath = Paths.get("src", "test", "resources", "testReportGoPath").toAbsolutePath(); + private final Path packagePath = Paths.get("github.com", "myOrg", "myProject"); + + @Test + void absolute_package_path_in_report() throws IOException { + Path packageAbsPath = goPath.resolve("src").resolve(packagePath); + + GoTestSensor goTestSensor = new GoTestSensor(); + goTestSensor.goPathContext = new GoPathContext(File.separatorChar, File.pathSeparator, null); + String transformedPackageAbsPath; + if (File.pathSeparator.equals(":")) { + transformedPackageAbsPath = "_" + packageAbsPath; + } else { + transformedPackageAbsPath = "_\\" + packageAbsPath.toString().replaceFirst(":", "_"); + } + TestInfo testInfo = new TestInfo("pass", transformedPackageAbsPath, "TestFoo", 42.); + + SensorContextTester contextTester = SensorContextTester.create(packageAbsPath); + DefaultFileSystem fs = contextTester.fileSystem(); + DefaultInputFile testFile = getTestInputFile(fs, "func TestFoo(", "foo_test.go"); + + InputFile foundTestFile = goTestSensor.findTestFile(fs, testInfo); + assertThat(foundTestFile).isEqualTo(testFile); + } + + @Test + void relative_package_path_in_report() throws IOException { + GoTestSensor goTestSensor = new GoTestSensor(); + goTestSensor.goPathContext = new GoPathContext(File.separatorChar, File.pathSeparator, goPath.toString()); + + TestInfo testInfo = new TestInfo("pass", packagePath.toString(), "TestFoo", 42.); + + Path baseDir = goPath.resolve("src").resolve(packagePath); + SensorContextTester contextTester = SensorContextTester.create(baseDir); + DefaultFileSystem fs = contextTester.fileSystem(); + DefaultInputFile testFile = getTestInputFile(fs, "func TestFoo(", "foo_test.go"); + + InputFile foundTestFile = goTestSensor.findTestFile(fs, testInfo); + assertThat(foundTestFile).isEqualTo(testFile); + } + + @Test + void invalid_package_path_in_report() throws IOException { + Path nestedPackagePath = packagePath.resolve("packageFoo"); + + GoTestSensor goTestSensor = new GoTestSensor(); + goTestSensor.goPathContext = new GoPathContext(File.separatorChar, File.pathSeparator, null); + + TestInfo testInfoTop = new TestInfo("pass", packagePath.toString(), "TestFoo", 42.); + TestInfo testInfoNested = new TestInfo("pass", nestedPackagePath.toString(), "TestFoo", 42.); + + Path baseDir = Paths.get("src", "test", "resources", "myProject").toAbsolutePath(); + SensorContextTester contextTester = SensorContextTester.create(baseDir); + + DefaultFileSystem fs = contextTester.fileSystem(); + + DefaultInputFile topTestFile = getTestInputFile(fs, "func TestFoo(", "foo_test.go"); + DefaultInputFile nestedTestFile = getTestInputFile(fs, "\nfunc TestFoo (", "packageFoo/foo_test.go"); + + InputFile foundTestFile; + foundTestFile = goTestSensor.findTestFile(fs, testInfoTop); + assertThat(foundTestFile).isEqualTo(topTestFile); + + foundTestFile = goTestSensor.findTestFile(fs, testInfoNested); + assertThat(foundTestFile).isEqualTo(nestedTestFile); + } + + @Test + void test_describe() { + GoTestSensor goTestSensor = new GoTestSensor(); + DefaultSensorDescriptor descriptor = new DefaultSensorDescriptor(); + goTestSensor.describe(descriptor); + + assertThat(descriptor.name()).isEqualTo("Go Unit Test Report"); + assertThat(descriptor.languages()).containsOnly("go"); + } + + @Test + void import_report() { + GoTestSensor goTestSensor = new GoTestSensor(); + goTestSensor.goPathContext = new GoPathContext(File.separatorChar, File.pathSeparator, goPath.toString()); + + Path baseDir = goPath.resolve("src").resolve(packagePath); + + SensorContextTester context = SensorContextTester.create(baseDir); + DefaultFileSystem fs = context.fileSystem(); + DefaultInputFile fooTestFile = getTestInputFile(fs, "something \nfunc TestFoo1( \nfunc TestFoo2( ", "foo_test.go"); + DefaultInputFile barTestFile = getTestInputFile(fs, "func TestBar(", "bar_test.go"); + + MapSettings settings = new MapSettings(); + String absoluteReportPath = baseDir.resolve("report1.out").toString(); + settings.setProperty(GoTestSensor.REPORT_PATH_KEY, "report.out,invilid/report/path," + absoluteReportPath); + context.setSettings(settings); + + goTestSensor.execute(context); + + assertThat(context.measure(fooTestFile.key(), CoreMetrics.TESTS).value()).isEqualTo(3); // one test comes from report1.out + assertThat(context.measure(fooTestFile.key(), CoreMetrics.SKIPPED_TESTS).value()).isZero(); + assertThat(context.measure(fooTestFile.key(), CoreMetrics.TEST_FAILURES).value()).isEqualTo(1); + assertThat(context.measure(fooTestFile.key(), CoreMetrics.TEST_ERRORS)).isNull(); + assertThat(context.measure(fooTestFile.key(), CoreMetrics.TEST_EXECUTION_TIME).value()).isEqualTo(4); + + // TestBar is present two times, once with a correct package path, and once with an incorrect one. + // We can not differentiate the second invalid path from a module name (see go_module_report_test), + // so we consider it as valid. It is really unlikely to happen in a real situation. + assertThat(context.measure(barTestFile.key(), CoreMetrics.TESTS).value()).isEqualTo(2); + assertThat(context.measure(barTestFile.key(), CoreMetrics.SKIPPED_TESTS).value()).isEqualTo(2); + assertThat(context.measure(barTestFile.key(), CoreMetrics.TEST_FAILURES).value()).isZero(); + assertThat(context.measure(barTestFile.key(), CoreMetrics.TEST_ERRORS)).isNull(); + assertThat(context.measure(barTestFile.key(), CoreMetrics.TEST_EXECUTION_TIME).value()).isEqualTo(7 + 7); + assertThat(String.join("\n", logTester.logs(Level.ERROR))) + .contains("Test report can't be loaded, file not found"); + } + + private DefaultInputFile getTestInputFile(DefaultFileSystem fs, String content, String relativePath) { + DefaultInputFile nestedTestFile = new TestInputFileBuilder("moduleKey", relativePath) + .setLanguage("go") + .setType(Type.TEST) + .setContents(content) + .build(); + fs.add(nestedTestFile); + return nestedTestFile; + } + + + @Test + void subtests() throws Exception { + GoTestSensor goTestSensor = new GoTestSensor(); + goTestSensor.goPathContext = new GoPathContext(File.separatorChar, File.pathSeparator, goPath.toString()); + + Path baseDir = goPath.resolve("src").resolve(packagePath); + + SensorContextTester context = SensorContextTester.create(baseDir); + DefaultFileSystem fs = context.fileSystem(); + DefaultInputFile mulTestFile = getTestInputFile(fs, new String(Files.readAllBytes(baseDir.resolve("mul_test.go"))), "mul_test.go"); + + MapSettings settings = new MapSettings(); + String absoluteReportPath = baseDir.resolve("subtest_report.json").toString(); + settings.setProperty(GoTestSensor.REPORT_PATH_KEY, absoluteReportPath); + context.setSettings(settings); + + goTestSensor.execute(context); + + assertThat(context.measure(mulTestFile.key(), CoreMetrics.TESTS).value()).isEqualTo(4); + } + + @Test + void go_module_report_test() throws Exception { + GoTestSensor goTestSensor = new GoTestSensor(); + goTestSensor.goPathContext = new GoPathContext(File.separatorChar, File.pathSeparator, goPath.toString()); + + Path baseDir = goPath.resolve("src").resolve(packagePath); + + SensorContextTester context = SensorContextTester.create(baseDir); + DefaultFileSystem fs = context.fileSystem(); + DefaultInputFile mulTestFile = getTestInputFile(fs, new String(Files.readAllBytes(baseDir.resolve("mul_test.go"))), "mul_test.go"); + + MapSettings settings = new MapSettings(); + // Reports created from Go projects with modules contains the module name instead of the package path in the "Package". Ex: + // "my/module/subpackage" is in fact referring to the subpackage folder. + // "my/module" is referring to the root. + String absoluteReportPath = baseDir.resolve("module_report.json").toString(); + settings.setProperty(GoTestSensor.REPORT_PATH_KEY, absoluteReportPath); + context.setSettings(settings); + + goTestSensor.execute(context); + + assertThat(context.measure(mulTestFile.key(), CoreMetrics.TESTS).value()).isEqualTo(4); + } +} diff --git a/sonar-go-plugin/src/test/resources/checks/CodeAfterJumpGoCheck.go b/sonar-go-plugin/src/test/resources/checks/CodeAfterJumpGoCheck.go new file mode 100644 index 00000000..159e4d3d --- /dev/null +++ b/sonar-go-plugin/src/test/resources/checks/CodeAfterJumpGoCheck.go @@ -0,0 +1,74 @@ +// S1763 +package samples + +import "fmt" + +func statement_after_return() { + return // Noncompliant + fmt.Print("Will not execute") +} + +func return_after_return() { + return // Noncompliant + return +} + +func statement_after_multi_values_return() (int, string) { + return 42, "42" // Noncompliant + fmt.Print("Will not execute") +} + +func label_after_return(x int) int { + if (x > 10) { + goto Foo + } + return 42 // Compliant +Foo: + return 21 +} + +func invalid_statement_after_return_in_label(x int) int { + if (x > 10) { + goto Foo + } + return 42 +Foo: + return 21 // Compliant - FN: labels are not properly supported + fmt.Print("Will not execute") +} + +func invalid_statement_after_return_in_label_block(x int) int { + if (x > 10) { + goto Foo + } + return 42 +Foo: + // Multiple statements after a label are analysed correctly only if they are inside a block "{ }" + { + return 21 // Noncompliant + fmt.Print("Will not execute") + } +} + +func return_simple(){ + return // Compliant +} + +func return_with_semicolon() int{ + return 0; // Compliant +} + +func return_with_semicolon_and_empty_statement() int{ + return 0;; // Noncompliant +} + +func return_followed_by_return() int{ + return 0; // Noncompliant + return 0; +} + +func return_semicolon_label() int{ + return 0; // Compliant +Foo: + return 1; +} diff --git a/sonar-go-plugin/src/test/resources/checks/DuplicateBranchGoCheck.go b/sonar-go-plugin/src/test/resources/checks/DuplicateBranchGoCheck.go new file mode 100644 index 00000000..39b0d4b9 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/checks/DuplicateBranchGoCheck.go @@ -0,0 +1,91 @@ +package band + +type Header interface { + Print() string +} + +type Common struct { + field1 int + field2 int + field3 int +} + +type Bar struct { + field1 int + field2 int + field3 int + barSpecific string +} + +func (b Bar) Print() string { + return "This is a Bar" +} + +type Foo struct { + field1 int + field2 int + field3 int + fooSpecific string +} + +func (f Foo) Print() string { + return "This is a Foo" +} + +func ProcessHeader(header Header) Common { + switch input := header.(type) { + case Foo: + return Common{ + field1: input.field1, + field2: input.field2, + field3: input.field3, + } + case Bar: + return Common{ // Compliant + field1: input.field1, + field2: input.field2, + field3: input.field3, + } + default: + panic("...") + } +} + +func PrintsExpectedMessage(header Header) bool { + printed := header.Print() + switch printed { + case "This is a Bar": + tmp := true + return tmp + case "This is a Foo": + tmp := true // Noncompliant + return tmp + default: + return false + } +} + +func UnrelatedTest(common Common) bool { + sum := 0 + if common.field1 > common.field2 { + sum += common.field1 + common.field2 + common.field3 = sum * sum + } else if common.field1 == common.field2 { // Noncompliant + sum += common.field1 + common.field2 + common.field3 = sum * sum + } + return sum > 42 +} + +func SwitchWithLongExpression() bool { + switch header.Print() { + case "This is a Bar": + tmp := true + return tmp + case "This is a Foo": + tmp := true // Noncompliant + return tmp + default: + return false + } +} diff --git a/sonar-go-plugin/src/test/resources/checks/OneStatementPerLineGoCheck.go b/sonar-go-plugin/src/test/resources/checks/OneStatementPerLineGoCheck.go new file mode 100644 index 00000000..446386a0 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/checks/OneStatementPerLineGoCheck.go @@ -0,0 +1,17 @@ +// S122 +package samples + +func foo() {} +func bar() {} + +func two_statements_per_line() { + foo(); bar(); // Noncompliant +} + +func single_statement_per_line(x int) { + x = 42 +} + +func single_statement_per_line_with_semicolon(x int) { + x = 42; +} diff --git a/sonar-go-plugin/src/test/resources/coverage/cover.go b/sonar-go-plugin/src/test/resources/coverage/cover.go new file mode 100644 index 00000000..13ceff92 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/cover.go @@ -0,0 +1,9 @@ +package pkg1 + +func func1(a, b int ) int { + if a > 0 { + if b > 0 { return a + 1 } else { return 0 } + } else { + return -1 + } +} diff --git a/sonar-go-plugin/src/test/resources/coverage/cover_empty_lines.go b/sonar-go-plugin/src/test/resources/coverage/cover_empty_lines.go new file mode 100644 index 00000000..ce71440a --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/cover_empty_lines.go @@ -0,0 +1,9 @@ +package pkg1 + +func Hello(sum int) string { + + sum++ + + return "Hello, world." + +} diff --git a/sonar-go-plugin/src/test/resources/coverage/cover_test.go b/sonar-go-plugin/src/test/resources/coverage/cover_test.go new file mode 100644 index 00000000..52c4653e --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/cover_test.go @@ -0,0 +1,11 @@ +package pkg1 + +import "testing" + +func Test_func1(t *testing.T) { + expected := 2 + actual := func1(1, 2) + if expected != actual { + t.Fatalf("got %v; expected %v", actual, expected) + } +} diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.fuzzy.out b/sonar-go-plugin/src/test/resources/coverage/coverage.fuzzy.out new file mode 100644 index 00000000..accaf75b --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.fuzzy.out @@ -0,0 +1,7 @@ +mode: set +fuzzy/dir/cover.go:3.27,4.11 1 1 +fuzzy/dir/cover.go:4.11,5.12 1 1 +fuzzy/dir/cover.go:5.12,5.28 1 1 +fuzzy/dir/cover.go:5.28,5.46 1 0 +fuzzy/dir/cover.go:6.3,11.3 1 0 +doesntexists.go:6.3,11.3 1 0 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.linux.absolute.out b/sonar-go-plugin/src/test/resources/coverage/coverage.linux.absolute.out new file mode 100644 index 00000000..70deb102 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.linux.absolute.out @@ -0,0 +1,6 @@ +mode: atomic +_/home/paul/dev/github/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:3.27,4.11 1 1 +_/home/paul/dev/github/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:4.11,5.12 1 1 +_/home/paul/dev/github/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:5.12,5.28 1 1 +_/home/paul/dev/github/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:5.28,5.46 1 0 +_/home/paul/dev/github/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:6.3,8.3 1 0 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.linux.relative.out b/sonar-go-plugin/src/test/resources/coverage/coverage.linux.relative.out new file mode 100644 index 00000000..19f702a8 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.linux.relative.out @@ -0,0 +1,6 @@ +mode: count +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:3.27,4.11 1 1 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:4.11,5.12 1 1 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:5.12,5.28 1 1 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:5.28,5.46 1 0 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:6.3,8.3 1 0 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.one.broken.line.out b/sonar-go-plugin/src/test/resources/coverage/coverage.one.broken.line.out new file mode 100644 index 00000000..148ecc69 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.one.broken.line.out @@ -0,0 +1,7 @@ +mode: count +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:3.27,4.11 1 1 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:4.11,5.12 1 1 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:5.12,5.28 1 1 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:5.28,5.46 1 0 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:6.3,8.3 1 0 +github.com/SonarSource/slang/sonar-go-plugin/src/test/resources/coverage/cover.go:5.12,5.28 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.relative.out b/sonar-go-plugin/src/test/resources/coverage/coverage.relative.out new file mode 100644 index 00000000..4070f02b --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.relative.out @@ -0,0 +1,6 @@ +mode: set +cover.go:3.27,4.11 1 1 +cover.go:4.11,5.12 1 1 +cover.go:5.12,5.28 1 1 +cover.go:5.28,5.46 1 0 +cover.go:6.3,11.3 1 0 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.switch.go b/sonar-go-plugin/src/test/resources/coverage/coverage.switch.go new file mode 100644 index 00000000..cfeb4d41 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.switch.go @@ -0,0 +1,15 @@ +package main + +func switchFunc(s string) string { + switch s { + case "a": + return "A" + case "b": + return "B" + case "c": + return "C" + case "d": + return "D" + } + return "" +} diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.switch.out b/sonar-go-plugin/src/test/resources/coverage/coverage.switch.out new file mode 100644 index 00000000..0b0c71a1 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.switch.out @@ -0,0 +1,7 @@ +mode: set +coverage.switch.go:3.34,4.12 1 1 +coverage.switch.go:5.14,6.17 1 1 +coverage.switch.go:7.14,8.17 1 0 +coverage.switch.go:9.14,10.17 1 0 +coverage.switch.go:11.14,12.17 1 0 +coverage.switch.go:14.3,14.12 1 0 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.win.absolute.out b/sonar-go-plugin/src/test/resources/coverage/coverage.win.absolute.out new file mode 100644 index 00000000..8c8c1a33 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.win.absolute.out @@ -0,0 +1,6 @@ +mode: set +_\C_\Users\paul\dev\github\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:3.27,4.11 1 1 +_\C_\Users\paul\dev\github\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:4.11,5.12 1 1 +_\C_\Users\paul\dev\github\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:5.12,5.28 1 1 +_\C_\Users\paul\dev\github\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:5.28,5.46 1 0 +_\C_\Users\paul\dev\github\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:6.3,8.3 1 0 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage.win.relative.out b/sonar-go-plugin/src/test/resources/coverage/coverage.win.relative.out new file mode 100644 index 00000000..eb7bf83e --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage.win.relative.out @@ -0,0 +1,6 @@ +mode: set +github.com\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:3.27,4.11 1 1 +github.com\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:4.11,5.12 1 1 +github.com\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:5.12,5.28 1 1 +github.com\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:5.28,5.46 1 0 +github.com\SonarSource\slang\sonar-go-plugin\src\test\resources\coverage\cover.go:6.3,8.3 1 0 diff --git a/sonar-go-plugin/src/test/resources/coverage/coverage1.out b/sonar-go-plugin/src/test/resources/coverage/coverage1.out new file mode 100644 index 00000000..8a77ec1c --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/coverage1.out @@ -0,0 +1 @@ +file used for wildcard pattern testing \ No newline at end of file diff --git a/sonar-go-plugin/src/test/resources/coverage/glob/coverage.glob.out b/sonar-go-plugin/src/test/resources/coverage/glob/coverage.glob.out new file mode 100644 index 00000000..8a77ec1c --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/glob/coverage.glob.out @@ -0,0 +1 @@ +file used for wildcard pattern testing \ No newline at end of file diff --git a/sonar-go-plugin/src/test/resources/coverage/test1/coverage.out b/sonar-go-plugin/src/test/resources/coverage/test1/coverage.out new file mode 100644 index 00000000..5488f8b7 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/coverage/test1/coverage.out @@ -0,0 +1,2 @@ +not valid format +{not valid \ No newline at end of file diff --git a/sonar-go-plugin/src/test/resources/externalreport/all-golint-report.txt b/sonar-go-plugin/src/test/resources/externalreport/all-golint-report.txt new file mode 100644 index 00000000..d05fdabe --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/all-golint-report.txt @@ -0,0 +1,102 @@ +main.go:1:1: a blank import should be only in a main or test package, or have a comment justifying it +main.go:1:1: comment on exported const InlineWhatever should be of the form "InlineWhatever ..." +main.go:1:1: comment on exported const Thing should be of the form "Thing ..." +main.go:1:1: comment on exported const WhatDoesHeDo should be of the form "WhatDoesHeDo ..." +main.go:1:1: comment on exported method U.G should be of the form "G ..." +main.go:1:1: comment on exported type U should be of the form "U ..." (with optional leading article) +main.go:1:1: const fooId should be fooID +main.go:1:1: context.Context should be the first parameter of a function +main.go:1:1: don't use ALL_CAPS in Go names; use CamelCase +main.go:1:1: don't use MixedCaps in package name; PkgName should be pkgname +main.go:1:1: don't use an underscore in package name +main.go:1:1: don't use leading k in Go names; const kLeadingKay should be leadingKay +main.go:1:1: don't use underscores in Go names; func WhyAreYouUsingCapitalLetters_InACFunctionName should be WhyAreYouUsingCapitalLettersInACFunctionName +main.go:1:1: don't use underscores in Go names; func f_it should be fIt +main.go:1:1: don't use underscores in Go names; func parameter bad_name should be badName +main.go:1:1: don't use underscores in Go names; func parameter but_use_go_param_names should be butUseGoParamNames +main.go:1:1: don't use underscores in Go names; func result no_way should be noWay +main.go:1:1: don't use underscores in Go names; interface method parameter foo_bar should be fooBar +main.go:1:1: don't use underscores in Go names; method parameter more_under should be moreUnder +main.go:1:1: don't use underscores in Go names; method result still_more should be stillMore +main.go:1:1: don't use underscores in Go names; struct field x_damn should be xDamn +main.go:1:1: don't use underscores in Go names; type t_wow should be tWow +main.go:1:1: don't use underscores in Go names; var more_underscore should be moreUnderscore +main.go:1:1: don't use underscores in Go names; var var_name should be varName +main.go:1:1: error should be the last type when returning multiple items +main.go:1:1: error strings should not be capitalized or end with punctuation or a newline +main.go:1:1: error var E2 should have name of the form ErrFoo +main.go:1:1: error var Exp should have name of the form ErrFoo +main.go:1:1: error var e1 should have name of the form errFoo +main.go:1:1: error var unexp should have name of the form errFoo +main.go:1:1: exported const SomeUndocumented should have comment (or a comment on this block) or be unexported +main.go:1:1: exported const UndocAgain should have comment or be unexported +main.go:1:1: exported const Whatsit should have comment (or a comment on this block) or be unexported +main.go:1:1: exported const X should have comment or be unexported +main.go:1:1: exported func Exported returns unexported type foo.hidden, which can be annoying to use +main.go:1:1: exported func ExportedIntReturner returns unexported type foo.int, which can be annoying to use +main.go:1:1: exported method MethodOnT returns unexported type foo.hidden, which can be annoying to use +main.go:1:1: exported method T.F should have comment or be unexported +main.go:1:1: exported method T.Len should have comment or be unexported +main.go:1:1: exported method U.Other should have comment or be unexported +main.go:1:1: exported method V.H should have comment or be unexported +main.go:1:1: exported type T should have comment or be unexported +main.go:1:1: exported var W should have comment or be unexported +main.go:1:1: exported var Z should have its own declaration +main.go:1:1: func name will be used as donut.DonutRank by other packages, and that stutters; consider calling this Rank +main.go:1:1: if block ends with a return statement, so drop this else and outdent its block +main.go:1:1: if block ends with a return statement, so drop this else and outdent its block (move short variable declaration to its own line if necessary) +main.go:1:1: package comment is detached; there should be no blank lines between it and the package statement +main.go:1:1: package comment should be of the form "Package testdata ..." +main.go:1:1: package comment should not have leading space +main.go:1:1: range var theIp should be theIP +main.go:1:1: receiver name a should be consistent with previous receiver name b for bar +main.go:1:1: receiver name should be a reflection of its identity; don't use generic names such as "this" or "self" +main.go:1:1: receiver name should not be an underscore, omit the name if it is unused +main.go:1:1: should drop = "" from declaration of var myZeroStr; it is the zero value +main.go:1:1: should drop = '\000' from declaration of var myZeroRune2; it is the zero value +main.go:1:1: should drop = '\x00' from declaration of var myZeroRune; it is the zero value +main.go:1:1: should drop = 0 from declaration of var myZeroInt; it is the zero value +main.go:1:1: should drop = 0. from declaration of var myZeroFlt; it is the zero value +main.go:1:1: should drop = 0.0 from declaration of var myZeroF64; it is the zero value +main.go:1:1: should drop = 0i from declaration of var myZeroImg; it is the zero value +main.go:1:1: should drop = `` from declaration of var myZeroRaw; it is the zero value +main.go:1:1: should drop = nil from declaration of var myZeroPtr; it is the zero value +main.go:1:1: should not use basic type bool as key in context.WithValue +main.go:1:1: should not use basic type byte as key in context.WithValue +main.go:1:1: should not use basic type complex128 as key in context.WithValue +main.go:1:1: should not use basic type complex64 as key in context.WithValue +main.go:1:1: should not use basic type float32 as key in context.WithValue +main.go:1:1: should not use basic type float64 as key in context.WithValue +main.go:1:1: should not use basic type int as key in context.WithValue +main.go:1:1: should not use basic type int16 as key in context.WithValue +main.go:1:1: should not use basic type int32 as key in context.WithValue +main.go:1:1: should not use basic type int64 as key in context.WithValue +main.go:1:1: should not use basic type int8 as key in context.WithValue +main.go:1:1: should not use basic type rune as key in context.WithValue +main.go:1:1: should not use basic type string as key in context.WithValue +main.go:1:1: should not use basic type uint as key in context.WithValue +main.go:1:1: should not use basic type uint16 as key in context.WithValue +main.go:1:1: should not use basic type uint32 as key in context.WithValue +main.go:1:1: should not use basic type uint64 as key in context.WithValue +main.go:1:1: should not use basic type uint8 as key in context.WithValue +main.go:1:1: should not use basic type uintptr as key in context.WithValue +main.go:1:1: should not use dot imports +main.go:1:1: should omit 2nd value from range; this loop is equivalent to `for x := range ...` +main.go:1:1: should omit 2nd value from range; this loop is equivalent to `for y = range ...` +main.go:1:1: should omit type *http.ServeMux from declaration of var mux; it will be inferred from the right-hand side +main.go:1:1: should omit type int from declaration of var myInt; it will be inferred from the right-hand side +main.go:1:1: should omit type io.Writer from declaration of var out2; it will be inferred from the right-hand side +main.go:1:1: should omit type string from declaration of var y; it will be inferred from the right-hand side +main.go:1:1: should omit values from range; this loop is equivalent to `for range ...` +main.go:1:1: should replace errors.New(fmt.Sprintf(...)) with fmt.Errorf(...) +main.go:1:1: should replace t.Error(fmt.Sprintf(...)) with t.Errorf(...) +main.go:1:1: should replace x += 1 with x++ +main.go:1:1: should replace y -= 1 with y-- +main.go:1:1: struct field Url should be URL +main.go:1:1: type name will be used as donut.DonutMaker by other packages, and that stutters; consider calling this Maker +main.go:1:1: var isEof should be isEOF +main.go:1:1: var myJson should be myJSON +main.go:1:1: var qId should be qID +main.go:1:1: var rpcTimeoutMsec is of type *time.Duration; don't use unit-specific suffix "Msec" +main.go:1:1: var tApi should be tAPI +main.go:1:1: var timeoutSecs is of type time.Duration; don't use unit-specific suffix "Secs" diff --git a/sonar-go-plugin/src/test/resources/externalreport/all-govet-report.txt b/sonar-go-plugin/src/test/resources/externalreport/all-govet-report.txt new file mode 100644 index 00000000..0d42a260 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/all-govet-report.txt @@ -0,0 +1,263 @@ +main.go:1: (i8 + 1) (8 bits) too small for shift of 8 +main.go:1: +build comment must appear before package clause and be followed by a blank line +main.go:1: Bad passes lock by value: testdata.CustomLock +main.go:1: Bad passes lock by value: testdata.L0 contains testdata.L1 contains testdata.L2 +main.go:1: Bad passes lock by value: testdata.LocalOnce contains sync.Mutex +main.go:1: BadFunc passes lock by value: sync.Mutex +main.go:1: BadFunc passes lock by value: testdata.EmbeddedRWMutex +main.go:1: BadFunc passes lock by value: testdata.FieldMutex contains sync.Mutex +main.go:1: BadFunc2 passes lock by value: sync.Map contains sync.Mutex +main.go:1: BadMeth passes lock by value: testdata.EmbeddedRWMutex +main.go:1: BadMeth passes lock by value: testdata.FieldMutex contains sync.Mutex +main.go:1: BenchmarkbadSuffix has malformed name: first letter after 'Benchmark' must not be lowercase +main.go:1: Error call has possible formatting directive %d +main.go:1: ExampleBuf refers to unknown identifier: Buf +main.go:1: ExampleBuf_Append refers to unknown identifier: Buf +main.go:1: ExampleBuf_Append_Bad has malformed example suffix: Bad +main.go:1: ExampleBuf_Append_Bad refers to unknown identifier: Buf +main.go:1: ExampleBuf_Append_suffix refers to unknown identifier: Buf +main.go:1: ExampleBuf_Clear refers to unknown field or method: Buf.Clear +main.go:1: ExampleBuf_Clear refers to unknown identifier: Buf +main.go:1: ExampleBuf_Len refers to unknown identifier: Buf +main.go:1: ExampleBuf_Len should be niladic +main.go:1: ExampleBuf_Reset refers to unknown identifier: Buf +main.go:1: ExampleBuf_Reset should return nothing +main.go:1: ExampleBuf_suffix refers to unknown identifier: Buf +main.go:1: ExamplePuffer refers to unknown identifier: Puffer +main.go:1: ExamplePuffer_Append refers to unknown identifier: Puffer +main.go:1: ExamplePuffer_suffix refers to unknown identifier: Puffer +main.go:1: Example_BadSuffix has malformed example suffix: BadSuffix +main.go:1: Log call has possible formatting directive %d +main.go:1: Logf format %d has arg "hi" of wrong type string +main.go:1: Printf call has arguments but no formatting directives +main.go:1: Printf call needs 1 arg but has 2 args +main.go:1: Printf call needs 2 args but has 4 args +main.go:1: Printf format % is missing verb at end of string +main.go:1: Printf format %*% uses non-int 0.22 as argument of * +main.go:1: Printf format %-10d reads arg #4, but call has only 3 args +main.go:1: Printf format %.*d uses non-int "hi" as argument of * +main.go:1: Printf format %.*d uses non-int s as argument of * +main.go:1: Printf format %.3v reads arg #3, but call has only 2 args +main.go:1: Printf format %6g has arg 'x' of wrong type rune +main.go:1: Printf format %E has arg true of wrong type bool +main.go:1: Printf format %F has arg 'x' of wrong type rune +main.go:1: Printf format %G has arg i of wrong type int +main.go:1: Printf format %U has arg x of wrong type float64 +main.go:1: Printf format %X has arg 2.3 of wrong type float64 +main.go:1: Printf format %[1][ has unknown verb [ +main.go:1: Printf format %[2]*.[1]*[3]d uses non-int "hi" as argument of * +main.go:1: Printf format %[xd is missing closing ] +main.go:1: Printf format %b has arg "hi" of wrong type string +main.go:1: Printf format %c has arg 2.3 of wrong type float64 +main.go:1: Printf format %d arg someFunction is a func value, not called +main.go:1: Printf format %d has arg ¬PercentDV of wrong type *testdata.notPercentDStruct +main.go:1: Printf format %d has arg 2.3 of wrong type float64 +main.go:1: Printf format %d has arg BoolFormatter(true) of wrong type testdata.BoolFormatter +main.go:1: Printf format %d has arg notPercentDV of wrong type testdata.notPercentDStruct +main.go:1: Printf format %e has arg "hi" of wrong type string +main.go:1: Printf format %f has arg "hi" of wrong type string +main.go:1: Printf format %g has arg "hi" of wrong type string +main.go:1: Printf format %g has arg imap of wrong type map[int]int +main.go:1: Printf format %o has arg x of wrong type float64 +main.go:1: Printf format %p has arg 23 of wrong type int +main.go:1: Printf format %q has arg notstringerarrayv of wrong type testdata.notstringerarray +main.go:1: Printf format %q has arg notstringerv of wrong type testdata.notstringer +main.go:1: Printf format %q has arg x of wrong type float64 +main.go:1: Printf format %s has arg &ue of wrong type *testdata.unexportedError +main.go:1: Printf format %s has arg &uef of wrong type *testdata.unexportedErrorOtherFields +main.go:1: Printf format %s has arg &us of wrong type *testdata.unexportedStringer +main.go:1: Printf format %s has arg &usf of wrong type *testdata.unexportedStringerOtherFields +main.go:1: Printf format %s has arg 123 of wrong type int +main.go:1: Printf format %s has arg b of wrong type bool +main.go:1: Printf format %s has arg byte(65) of wrong type byte +main.go:1: Printf format %s has arg embeddedStringerv of wrong type testdata.embeddedStringer +main.go:1: Printf format %s has arg intSlice of wrong type []int +main.go:1: Printf format %s has arg nonStringerArray of wrong type [1]testdata.unexportedStringer +main.go:1: Printf format %s has arg stringerv of wrong type testdata.ptrStringer +main.go:1: Printf format %s has arg uce of wrong type testdata.unexportedCustomError +main.go:1: Printf format %s has arg ue of wrong type testdata.unexportedError +main.go:1: Printf format %s has arg uef of wrong type testdata.unexportedErrorOtherFields +main.go:1: Printf format %s has arg uei of wrong type testdata.unexportedErrorInterface +main.go:1: Printf format %s has arg us of wrong type testdata.unexportedStringer +main.go:1: Printf format %s has arg usf of wrong type testdata.unexportedStringerOtherFields +main.go:1: Printf format %s reads arg #2, but call has only 1 arg +main.go:1: Printf format %t has arg 1 + 2i of wrong type complex128 +main.go:1: Printf format %t has arg 23 of wrong type int +main.go:1: Printf format %t has arg c of wrong type complex64 +main.go:1: Printf format %t has arg embeddedStringerv of wrong type testdata.embeddedStringer +main.go:1: Printf format %t has arg notstringerarrayv of wrong type testdata.notstringerarray +main.go:1: Printf format %t has arg notstringerv of wrong type testdata.notstringer +main.go:1: Printf format %t has arg stringerarrayv of wrong type testdata.stringerarray +main.go:1: Printf format %t has arg stringerv of wrong type testdata.ptrStringer +main.go:1: Printf format %v arg someFunction is a func value, not called +main.go:1: Printf format %x has arg nil of wrong type untyped nil +main.go:1: Printf format has invalid argument index [-2] +main.go:1: Printf format has invalid argument index [0] +main.go:1: Printf format has invalid argument index [2234234234234] +main.go:1: Printf format has invalid argument index [3] +main.go:1: Printf format has invalid argument index [x] +main.go:1: Println arg list ends with redundant newline +main.go:1: Println arg someFunction is a func value, not called +main.go:1: Println call has possible formatting directive %s +main.go:1: Println call has possible formatting directive %v +main.go:1: Println does not take io.Writer but has first arg os.Stdout +main.go:1: Sprintf call needs 1 arg but has 2 args +main.go:1: Sprintf format %v with arg &s causes recursive String method call +main.go:1: Sprintf format %v with arg s causes recursive String method call +main.go:1: Sprintf format has invalid argument index [3] +main.go:1: Sprintln arg p causes recursive call to String method +main.go:1: Sprintln arg s causes recursive call to String method +main.go:1: TestbadSuffix has malformed name: first letter after 'Test' must not be lowercase +main.go:1: TestemptyImportBadSuffix has malformed name: first letter after 'Test' must not be lowercase +main.go:1: assignment copies lock value to *p: sync.Mutex +main.go:1: assignment copies lock value to *tp: testdata.Tlock contains sync.Once contains sync.Mutex +main.go:1: assignment copies lock value to condY: sync.Cond contains sync.noCopy +main.go:1: assignment copies lock value to mmuA: sync.Mutex +main.go:1: assignment copies lock value to mmuB: sync.Mutex +main.go:1: assignment copies lock value to muA: sync.Mutex +main.go:1: assignment copies lock value to muB: sync.Mutex +main.go:1: assignment copies lock value to onceY: sync.Once contains sync.Mutex +main.go:1: assignment copies lock value to poolY: sync.Pool contains sync.noCopy +main.go:1: assignment copies lock value to rwmuY: sync.RWMutex +main.go:1: assignment copies lock value to t: testdata.Tlock contains sync.Once contains sync.Mutex +main.go:1: assignment copies lock value to wgY: sync.WaitGroup contains sync.noCopy +main.go:1: assignment copies lock value to y: sync.Mutex +main.go:1: call of Sizeof copies lock value: sync.Mutex +main.go:1: call of cap copies lock value: sync.Mutex +main.go:1: call of f copies lock value: struct{lock sync.Mutex} contains sync.Mutex +main.go:1: call of f copies lock value: sync.Mutex +main.go:1: call of fntab[0] copies lock value: struct{lock sync.Mutex} contains sync.Mutex +main.go:1: call of len copies lock value: sync.Mutex +main.go:1: call of new copies lock value: testdata.Tlock contains sync.Once contains sync.Mutex +main.go:1: call of unsafe.Sizeof copies lock value: sync.Mutex +main.go:1: comparison of function F != nil is always true +main.go:1: comparison of function F == nil is always false +main.go:1: comparison of function M != nil is always true +main.go:1: comparison of function M == nil is always false +main.go:1: declaration of "err" shadows declaration at testdata/shadow.go:13 +main.go:1: declaration of "x" shadows declaration at testdata/atomic.go:16 +main.go:1: declaration of "x" shadows declaration at testdata/shadow.go:14 +main.go:1: direct assignment to atomic value +main.go:1: flag.Flag composite literal uses unkeyed fields +main.go:1: func passes lock by value: sync.Map contains sync.Mutex +main.go:1: func passes lock by value: sync.Mutex +main.go:1: go/scanner.Error composite literal uses unkeyed fields +main.go:1: h (64 bits) too small for shift of 64 +main.go:1: i (64 bits) too small for shift of 64 +main.go:1: i16 (16 bits) too small for shift of 16 +main.go:1: i32 (32 bits) too small for shift of 32 +main.go:1: i64 (64 bits) too small for shift of 64 +main.go:1: i8 (8 bits) too small for shift of 8 +main.go:1: literal copies lock value from *tp: testdata.Tlock contains sync.Once contains sync.Mutex +main.go:1: literal copies lock value from *x: sync.Mutex +main.go:1: literal copies lock value from t: testdata.Tlock contains sync.Once contains sync.Mutex +main.go:1: loop variable f captured by func literal +main.go:1: loop variable i captured by func literal +main.go:1: loop variable j captured by func literal +main.go:1: loop variable p captured by func literal +main.go:1: loop variable v captured by func literal +main.go:1: method ReadByte() byte should have signature ReadByte() (byte, error) +main.go:1: method Scan(x fmt.ScanState, c byte) should have signature Scan(fmt.ScanState, rune) error +main.go:1: p (64 bits) too small for shift of 64 +main.go:1: possible malformed +build comment +main.go:1: possible misuse of unsafe.Pointer +main.go:1: possibly passing Go type with embedded pointer to C +main.go:1: range var k copies lock: sync.Mutex +main.go:1: range var m copies lock: sync.Mutex +main.go:1: range var mu copies lock: sync.Mutex +main.go:1: range var t.mu copies lock: sync.Mutex +main.go:1: range var v copies lock: sync.Mutex +main.go:1: redundant and: f != nil && f != nil +main.go:1: redundant and: i != 1 && i != 1 +main.go:1: redundant and: i == 0 && i == 0 +main.go:1: redundant or: f == nil || f == nil +main.go:1: redundant or: i != 0 || i != 0 +main.go:1: redundant or: i == 1 || i == 1 +main.go:1: redundant or: i == 1*2*3 || i == 1*2*3 +main.go:1: redundant or: i == j || i == j +main.go:1: redundant or: i+1 == 1 || i+1 == 1 +main.go:1: redundant or: j == 0 || j == 0 +main.go:1: redundant or: v == w || v == w +main.go:1: result of (bytes.Buffer).String call not used +main.go:1: result of (error).Error call not used +main.go:1: result of errors.New call not used +main.go:1: result of fmt.Errorf call not used +main.go:1: result of fmt.Sprint call not used +main.go:1: result of fmt.Sprintf call not used +main.go:1: return copies lock value: struct{lock sync.Mutex} contains sync.Mutex +main.go:1: return copies lock value: sync.Mutex +main.go:1: self-assignment of s.l[0] to s.l[0] +main.go:1: self-assignment of s.x to s.x +main.go:1: self-assignment of x to x +main.go:1: struct field AnonymousJSON repeats json tag "a" also at structtag.go:46 +main.go:1: struct field AnonymousXML repeats xml attribute tag "b" also at structtag.go:76 +main.go:1: struct field AnonymousXML repeats xml tag "a" also at structtag.go:60 +main.go:1: struct field DupAttr repeats xml attribute tag "b" also at structtag.go:76 +main.go:1: struct field DupOmitAttr repeats xml attribute tag "b" also at structtag.go:76 +main.go:1: struct field DuplicateJSON repeats json tag "a" also at structtag.go:46 +main.go:1: struct field DuplicateOmitJSON repeats json tag "a" also at structtag.go:46 +main.go:1: struct field DuplicateOmitXML repeats xml tag "a" also at structtag.go:60 +main.go:1: struct field DuplicateXML repeats xml tag "a" also at structtag.go:60 +main.go:1: struct field tag "ct\brl:\"char\"" not compatible with reflect.StructTag.Get: bad syntax for struct tag pair +main.go:1: struct field tag "x:`y`" not compatible with reflect.StructTag.Get: bad syntax for struct tag value +main.go:1: struct field tag ` x:"y"` not compatible with reflect.StructTag.Get: bad syntax for struct tag key +main.go:1: struct field tag `:"emptykey"` not compatible with reflect.StructTag.Get: bad syntax for struct tag key +main.go:1: struct field tag `hello` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair +main.go:1: struct field tag `json:"b, omitempty"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `json:"d,omitempty, string"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `x:"foo",y:"bar"` not compatible with reflect.StructTag.Get: key:"value" pairs not separated by spaces +main.go:1: struct field tag `x:"foo"y:"bar"` not compatible with reflect.StructTag.Get: key:"value" pairs not separated by spaces +main.go:1: struct field tag `x:"noEndQuote` not compatible with reflect.StructTag.Get: bad syntax for struct tag value +main.go:1: struct field tag `x:"trunc\x0"` not compatible with reflect.StructTag.Get: bad syntax for struct tag value +main.go:1: struct field tag `x:"y" x:"y"` not compatible with reflect.StructTag.Get: key:"value" pairs not separated by spaces +main.go:1: struct field tag `xml:" "` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:" g"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:" l local,omitempty"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:"f "` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:"h ,omitempty"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:"i, omitempty"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:"j local ,omitempty"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:"k local, omitempty"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field tag `xml:"m local,omitempty"` not compatible with reflect.StructTag.Get: suspicious space in struct tag value +main.go:1: struct field x has json tag but is not exported +main.go:1: struct field y has xml tag but is not exported +main.go:1: suspect and: 0 == i && 1 == i +main.go:1: suspect and: 0 == i && i == 1 +main.go:1: suspect and: i == 0 && 1 == i +main.go:1: suspect and: i == 0 && i == 1 +main.go:1: suspect or: "et" != "alii" || "et" != "cetera" +main.go:1: suspect or: 0 != i || 1 != i +main.go:1: suspect or: 0 != i || i != 1 +main.go:1: suspect or: i != 0 || 1 != i +main.go:1: suspect or: i != 0 || i != 1 +main.go:1: suspect or: i != 0 || i != 1<<4 +main.go:1: suspect or: i+3 != 7 || i+3 != 9 +main.go:1: suspect or: s != "one" || s != "the other" +main.go:1: the cancel function is not used on all paths (possible context leak) +main.go:1: the cancel function returned by context.WithCancel should be called, not discarded, to avoid a context leak +main.go:1: the cancel function returned by context.WithDeadline should be called, not discarded, to avoid a context leak +main.go:1: the cancel function returned by context.WithTimeout should be called, not discarded, to avoid a context leak +main.go:1: the cancel2 function is not used on all paths (possible context leak) +main.go:1: the cancel3 function is not used on all paths (possible context leak) +main.go:1: this return statement may be reached without using the cancel var defined on line 107 +main.go:1: this return statement may be reached without using the cancel var defined on line 131 +main.go:1: this return statement may be reached without using the cancel var defined on line 17 +main.go:1: this return statement may be reached without using the cancel var defined on line 42 +main.go:1: this return statement may be reached without using the cancel var defined on line 62 +main.go:1: this return statement may be reached without using the cancel var defined on line 91 +main.go:1: this return statement may be reached without using the cancel2 var defined on line 21 +main.go:1: this return statement may be reached without using the cancel3 var defined on line 27 +main.go:1: u (64 bits) too small for shift of 64 +main.go:1: u16 (16 bits) too small for shift of 16 +main.go:1: u32 (32 bits) too small for shift of 32 +main.go:1: u64 (64 bits) too small for shift of 64 +main.go:1: u8 (8 bits) too small for shift of 8 +main.go:1: unicode.CaseRange composite literal uses unkeyed fields +main.go:1: using res before checking for errors +main.go:1: using resp before checking for errors +main.go:1: variable declaration copies lock value to condYY: sync.Cond contains sync.noCopy +main.go:1: variable declaration copies lock value to onceYY: sync.Once contains sync.Mutex +main.go:1: variable declaration copies lock value to poolYY: sync.Pool contains sync.noCopy +main.go:1: variable declaration copies lock value to rwmuYY: sync.RWMutex +main.go:1: variable declaration copies lock value to wgYY: sync.WaitGroup contains sync.noCopy +main.go:1: variable declaration copies lock value to z: testdata.Tlock contains sync.Once contains sync.Mutex diff --git a/sonar-go-plugin/src/test/resources/externalreport/asm-govet-report.txt b/sonar-go-plugin/src/test/resources/externalreport/asm-govet-report.txt new file mode 100644 index 00000000..59e800ba --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/asm-govet-report.txt @@ -0,0 +1,734 @@ +main.go:1: [386] arg1: 4(SP) should be x+0(FP) +main.go:1: [386] arg1: 5(SP) should be y+1(FP) +main.go:1: [386] arg1: invalid MOVL of x+0(FP); int8 is 1-byte value +main.go:1: [386] arg1: invalid MOVL of y+1(FP); uint8 is 1-byte value +main.go:1: [386] arg1: invalid MOVQ of x+0(FP); int8 is 1-byte value +main.go:1: [386] arg1: invalid MOVQ of y+1(FP); uint8 is 1-byte value +main.go:1: [386] arg1: invalid MOVW of x+0(FP); int8 is 1-byte value +main.go:1: [386] arg1: invalid MOVW of y+1(FP); uint8 is 1-byte value +main.go:1: [386] arg1: invalid TESTL of x+0(FP); int8 is 1-byte value +main.go:1: [386] arg1: invalid TESTL of y+1(FP); uint8 is 1-byte value +main.go:1: [386] arg1: invalid TESTQ of x+0(FP); int8 is 1-byte value +main.go:1: [386] arg1: invalid TESTQ of y+1(FP); uint8 is 1-byte value +main.go:1: [386] arg1: invalid TESTW of x+0(FP); int8 is 1-byte value +main.go:1: [386] arg1: invalid TESTW of y+1(FP); uint8 is 1-byte value +main.go:1: [386] arg1: invalid offset x+1(FP); expected x+0(FP) +main.go:1: [386] arg1: invalid offset y+2(FP); expected y+1(FP) +main.go:1: [386] arg1: use of 6(SP) points beyond argument frame +main.go:1: [386] arg2: invalid MOVB of x+0(FP); int16 is 2-byte value +main.go:1: [386] arg2: invalid MOVB of y+2(FP); uint16 is 2-byte value +main.go:1: [386] arg2: invalid MOVL of x+0(FP); int16 is 2-byte value +main.go:1: [386] arg2: invalid MOVL of y+2(FP); uint16 is 2-byte value +main.go:1: [386] arg2: invalid MOVQ of x+0(FP); int16 is 2-byte value +main.go:1: [386] arg2: invalid MOVQ of y+2(FP); uint16 is 2-byte value +main.go:1: [386] arg2: invalid TESTB of x+0(FP); int16 is 2-byte value +main.go:1: [386] arg2: invalid TESTB of y+2(FP); uint16 is 2-byte value +main.go:1: [386] arg2: invalid TESTL of x+0(FP); int16 is 2-byte value +main.go:1: [386] arg2: invalid TESTL of y+2(FP); uint16 is 2-byte value +main.go:1: [386] arg2: invalid TESTQ of x+0(FP); int16 is 2-byte value +main.go:1: [386] arg2: invalid TESTQ of y+2(FP); uint16 is 2-byte value +main.go:1: [386] arg2: invalid offset x+2(FP); expected x+0(FP) +main.go:1: [386] arg2: invalid offset y+0(FP); expected y+2(FP) +main.go:1: [386] arg4: invalid MOVB of x+0(FP); int32 is 4-byte value +main.go:1: [386] arg4: invalid MOVB of y+4(FP); uint32 is 4-byte value +main.go:1: [386] arg4: invalid MOVQ of x+0(FP); int32 is 4-byte value +main.go:1: [386] arg4: invalid MOVQ of y+4(FP); uint32 is 4-byte value +main.go:1: [386] arg4: invalid MOVW of x+0(FP); int32 is 4-byte value +main.go:1: [386] arg4: invalid MOVW of y+4(FP); uint32 is 4-byte value +main.go:1: [386] arg4: invalid TESTB of x+0(FP); int32 is 4-byte value +main.go:1: [386] arg4: invalid TESTB of y+4(FP); uint32 is 4-byte value +main.go:1: [386] arg4: invalid TESTQ of x+0(FP); int32 is 4-byte value +main.go:1: [386] arg4: invalid TESTQ of y+4(FP); uint32 is 4-byte value +main.go:1: [386] arg4: invalid TESTW of x+0(FP); int32 is 4-byte value +main.go:1: [386] arg4: invalid TESTW of y+4(FP); uint32 is 4-byte value +main.go:1: [386] arg4: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [386] arg4: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [386] arg4: wrong argument size 2; expected $...-8 +main.go:1: [386] arg8: invalid MOVB of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [386] arg8: invalid MOVB of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [386] arg8: invalid MOVL of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [386] arg8: invalid MOVL of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [386] arg8: invalid MOVW of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [386] arg8: invalid MOVW of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [386] arg8: invalid TESTB of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [386] arg8: invalid TESTB of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [386] arg8: invalid TESTL of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [386] arg8: invalid TESTL of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [386] arg8: invalid TESTW of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [386] arg8: invalid TESTW of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [386] arg8: invalid offset x+8(FP); expected x+0(FP), x_lo+0(FP), or x_hi+4(FP) +main.go:1: [386] arg8: invalid offset y+2(FP); expected y+8(FP), y_lo+8(FP), or y_hi+12(FP) +main.go:1: [386] arg8: wrong argument size 2; expected $...-16 +main.go:1: [386] argiface: invalid MOVQ of x+0(FP); interface type is 4-byte value containing x_type+0(FP) and x_data+4(FP) +main.go:1: [386] argiface: invalid MOVQ of x_data+4(FP); interface data is 4-byte value +main.go:1: [386] argiface: invalid MOVQ of x_type+0(FP); interface type is 4-byte value +main.go:1: [386] argiface: invalid MOVQ of y+8(FP); interface itable is 4-byte value containing y_itable+8(FP) and y_data+12(FP) +main.go:1: [386] argiface: invalid MOVQ of y_data+12(FP); interface data is 4-byte value +main.go:1: [386] argiface: invalid MOVQ of y_itable+8(FP); interface itable is 4-byte value +main.go:1: [386] argiface: invalid MOVW of x+0(FP); interface type is 4-byte value containing x_type+0(FP) and x_data+4(FP) +main.go:1: [386] argiface: invalid MOVW of x_data+4(FP); interface data is 4-byte value +main.go:1: [386] argiface: invalid MOVW of x_type+0(FP); interface type is 4-byte value +main.go:1: [386] argiface: invalid MOVW of y+8(FP); interface itable is 4-byte value containing y_itable+8(FP) and y_data+12(FP) +main.go:1: [386] argiface: invalid MOVW of y_data+12(FP); interface data is 4-byte value +main.go:1: [386] argiface: invalid MOVW of y_itable+8(FP); interface itable is 4-byte value +main.go:1: [386] argiface: invalid offset x_data+0(FP); expected x_data+4(FP) +main.go:1: [386] argiface: invalid offset y_data+8(FP); expected y_data+12(FP) +main.go:1: [386] argiface: unknown variable x_itable; offset 0 is x_type+0(FP) +main.go:1: [386] argiface: unknown variable x_itable; offset 1 is x_type+0(FP) +main.go:1: [386] argiface: unknown variable y_type; offset 8 is y_itable+8(FP) +main.go:1: [386] argint: invalid MOVB of x+0(FP); int is 4-byte value +main.go:1: [386] argint: invalid MOVB of y+4(FP); uint is 4-byte value +main.go:1: [386] argint: invalid MOVQ of x+0(FP); int is 4-byte value +main.go:1: [386] argint: invalid MOVQ of y+4(FP); uint is 4-byte value +main.go:1: [386] argint: invalid MOVW of x+0(FP); int is 4-byte value +main.go:1: [386] argint: invalid MOVW of y+4(FP); uint is 4-byte value +main.go:1: [386] argint: invalid TESTB of x+0(FP); int is 4-byte value +main.go:1: [386] argint: invalid TESTB of y+4(FP); uint is 4-byte value +main.go:1: [386] argint: invalid TESTQ of x+0(FP); int is 4-byte value +main.go:1: [386] argint: invalid TESTQ of y+4(FP); uint is 4-byte value +main.go:1: [386] argint: invalid TESTW of x+0(FP); int is 4-byte value +main.go:1: [386] argint: invalid TESTW of y+4(FP); uint is 4-byte value +main.go:1: [386] argint: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [386] argint: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [386] argint: wrong argument size 2; expected $...-8 +main.go:1: [386] argptr: invalid MOVB of x+0(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid MOVB of y+4(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid MOVQ of x+0(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid MOVQ of y+4(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid MOVW of c+8(FP); chan int is 4-byte value +main.go:1: [386] argptr: invalid MOVW of f+16(FP); func() is 4-byte value +main.go:1: [386] argptr: invalid MOVW of m+12(FP); map[int]int is 4-byte value +main.go:1: [386] argptr: invalid MOVW of x+0(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid MOVW of y+4(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid TESTB of x+0(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid TESTB of y+4(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid TESTQ of x+0(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid TESTQ of y+4(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid TESTW of x+0(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid TESTW of y+4(FP); *byte is 4-byte value +main.go:1: [386] argptr: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [386] argptr: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [386] argptr: wrong argument size 2; expected $...-20 +main.go:1: [386] argslice: invalid MOVQ of x+0(FP); slice base is 4-byte value containing x_base+0(FP), x_len+4(FP), and x_cap+8(FP) +main.go:1: [386] argslice: invalid MOVQ of x_base+0(FP); slice base is 4-byte value +main.go:1: [386] argslice: invalid MOVQ of x_cap+8(FP); slice cap is 4-byte value +main.go:1: [386] argslice: invalid MOVQ of x_len+4(FP); slice len is 4-byte value +main.go:1: [386] argslice: invalid MOVW of x+0(FP); slice base is 4-byte value containing x_base+0(FP), x_len+4(FP), and x_cap+8(FP) +main.go:1: [386] argslice: invalid MOVW of x_base+0(FP); slice base is 4-byte value +main.go:1: [386] argslice: invalid MOVW of x_cap+8(FP); slice cap is 4-byte value +main.go:1: [386] argslice: invalid MOVW of x_len+4(FP); slice len is 4-byte value +main.go:1: [386] argslice: invalid offset x_cap+0(FP); expected x_cap+8(FP) +main.go:1: [386] argslice: invalid offset x_len+0(FP); expected x_len+4(FP) +main.go:1: [386] argslice: invalid offset y+0(FP); expected y+12(FP), y_base+12(FP), y_len+16(FP), or y_cap+20(FP) +main.go:1: [386] argslice: invalid offset y_cap+8(FP); expected y_cap+20(FP) +main.go:1: [386] argslice: invalid offset y_len+4(FP); expected y_len+16(FP) +main.go:1: [386] argslice: wrong argument size 0; expected $...-24 +main.go:1: [386] argstring: invalid MOVQ of x+0(FP); string base is 4-byte value containing x_base+0(FP) and x_len+4(FP) +main.go:1: [386] argstring: invalid MOVQ of x_base+0(FP); string base is 4-byte value +main.go:1: [386] argstring: invalid MOVQ of x_len+4(FP); string len is 4-byte value +main.go:1: [386] argstring: invalid MOVW of x+0(FP); string base is 4-byte value containing x_base+0(FP) and x_len+4(FP) +main.go:1: [386] argstring: invalid MOVW of x_base+0(FP); string base is 4-byte value +main.go:1: [386] argstring: invalid MOVW of x_len+4(FP); string len is 4-byte value +main.go:1: [386] argstring: invalid offset x_len+0(FP); expected x_len+4(FP) +main.go:1: [386] argstring: invalid offset y+0(FP); expected y+8(FP), y_base+8(FP), or y_len+12(FP) +main.go:1: [386] argstring: invalid offset y_len+4(FP); expected y_len+12(FP) +main.go:1: [386] argstring: wrong argument size 0; expected $...-16 +main.go:1: [386] returnbyte: invalid MOVL of ret+4(FP); byte is 1-byte value +main.go:1: [386] returnbyte: invalid MOVQ of ret+4(FP); byte is 1-byte value +main.go:1: [386] returnbyte: invalid MOVW of ret+4(FP); byte is 1-byte value +main.go:1: [386] returnbyte: invalid offset ret+3(FP); expected ret+4(FP) +main.go:1: [386] returnint: invalid MOVB of ret+0(FP); int is 4-byte value +main.go:1: [386] returnint: invalid MOVQ of ret+0(FP); int is 4-byte value +main.go:1: [386] returnint: invalid MOVW of ret+0(FP); int is 4-byte value +main.go:1: [386] returnint: invalid offset ret+1(FP); expected ret+0(FP) +main.go:1: [386] returnint: unknown variable r; offset 0 is ret+0(FP) +main.go:1: [386] returnintmissing: RET without writing to 4-byte ret+0(FP) +main.go:1: [386] returnnamed: invalid MOVQ of r1+4(FP); int is 4-byte value +main.go:1: [amd64] arg1: 8(SP) should be x+0(FP) +main.go:1: [amd64] arg1: 9(SP) should be y+1(FP) +main.go:1: [amd64] arg1: invalid MOVL of x+0(FP); int8 is 1-byte value +main.go:1: [amd64] arg1: invalid MOVL of y+1(FP); uint8 is 1-byte value +main.go:1: [amd64] arg1: invalid MOVQ of x+0(FP); int8 is 1-byte value +main.go:1: [amd64] arg1: invalid MOVQ of y+1(FP); uint8 is 1-byte value +main.go:1: [amd64] arg1: invalid MOVW of x+0(FP); int8 is 1-byte value +main.go:1: [amd64] arg1: invalid MOVW of y+1(FP); uint8 is 1-byte value +main.go:1: [amd64] arg1: invalid TESTL of x+0(FP); int8 is 1-byte value +main.go:1: [amd64] arg1: invalid TESTL of y+1(FP); uint8 is 1-byte value +main.go:1: [amd64] arg1: invalid TESTQ of x+0(FP); int8 is 1-byte value +main.go:1: [amd64] arg1: invalid TESTQ of y+1(FP); uint8 is 1-byte value +main.go:1: [amd64] arg1: invalid TESTW of x+0(FP); int8 is 1-byte value +main.go:1: [amd64] arg1: invalid TESTW of y+1(FP); uint8 is 1-byte value +main.go:1: [amd64] arg1: invalid offset x+1(FP); expected x+0(FP) +main.go:1: [amd64] arg1: invalid offset y+2(FP); expected y+1(FP) +main.go:1: [amd64] arg1: use of 10(SP) points beyond argument frame +main.go:1: [amd64] arg2: invalid MOVB of x+0(FP); int16 is 2-byte value +main.go:1: [amd64] arg2: invalid MOVB of y+2(FP); uint16 is 2-byte value +main.go:1: [amd64] arg2: invalid MOVL of x+0(FP); int16 is 2-byte value +main.go:1: [amd64] arg2: invalid MOVL of y+2(FP); uint16 is 2-byte value +main.go:1: [amd64] arg2: invalid MOVQ of x+0(FP); int16 is 2-byte value +main.go:1: [amd64] arg2: invalid MOVQ of y+2(FP); uint16 is 2-byte value +main.go:1: [amd64] arg2: invalid TESTB of x+0(FP); int16 is 2-byte value +main.go:1: [amd64] arg2: invalid TESTB of y+2(FP); uint16 is 2-byte value +main.go:1: [amd64] arg2: invalid TESTL of x+0(FP); int16 is 2-byte value +main.go:1: [amd64] arg2: invalid TESTL of y+2(FP); uint16 is 2-byte value +main.go:1: [amd64] arg2: invalid TESTQ of x+0(FP); int16 is 2-byte value +main.go:1: [amd64] arg2: invalid TESTQ of y+2(FP); uint16 is 2-byte value +main.go:1: [amd64] arg2: invalid offset x+2(FP); expected x+0(FP) +main.go:1: [amd64] arg2: invalid offset y+0(FP); expected y+2(FP) +main.go:1: [amd64] arg4: invalid MOVB of x+0(FP); int32 is 4-byte value +main.go:1: [amd64] arg4: invalid MOVB of y+4(FP); uint32 is 4-byte value +main.go:1: [amd64] arg4: invalid MOVQ of x+0(FP); int32 is 4-byte value +main.go:1: [amd64] arg4: invalid MOVQ of y+4(FP); uint32 is 4-byte value +main.go:1: [amd64] arg4: invalid MOVW of x+0(FP); int32 is 4-byte value +main.go:1: [amd64] arg4: invalid MOVW of y+4(FP); uint32 is 4-byte value +main.go:1: [amd64] arg4: invalid TESTB of x+0(FP); int32 is 4-byte value +main.go:1: [amd64] arg4: invalid TESTB of y+4(FP); uint32 is 4-byte value +main.go:1: [amd64] arg4: invalid TESTQ of x+0(FP); int32 is 4-byte value +main.go:1: [amd64] arg4: invalid TESTQ of y+4(FP); uint32 is 4-byte value +main.go:1: [amd64] arg4: invalid TESTW of x+0(FP); int32 is 4-byte value +main.go:1: [amd64] arg4: invalid TESTW of y+4(FP); uint32 is 4-byte value +main.go:1: [amd64] arg4: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [amd64] arg4: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [amd64] arg4: wrong argument size 2; expected $...-8 +main.go:1: [amd64] arg8: invalid MOVB of x+0(FP); int64 is 8-byte value +main.go:1: [amd64] arg8: invalid MOVB of y+8(FP); uint64 is 8-byte value +main.go:1: [amd64] arg8: invalid MOVL of x+0(FP); int64 is 8-byte value +main.go:1: [amd64] arg8: invalid MOVL of y+8(FP); uint64 is 8-byte value +main.go:1: [amd64] arg8: invalid MOVW of x+0(FP); int64 is 8-byte value +main.go:1: [amd64] arg8: invalid MOVW of y+8(FP); uint64 is 8-byte value +main.go:1: [amd64] arg8: invalid TESTB of x+0(FP); int64 is 8-byte value +main.go:1: [amd64] arg8: invalid TESTB of y+8(FP); uint64 is 8-byte value +main.go:1: [amd64] arg8: invalid TESTL of x+0(FP); int64 is 8-byte value +main.go:1: [amd64] arg8: invalid TESTL of y+8(FP); uint64 is 8-byte value +main.go:1: [amd64] arg8: invalid TESTW of x+0(FP); int64 is 8-byte value +main.go:1: [amd64] arg8: invalid TESTW of y+8(FP); uint64 is 8-byte value +main.go:1: [amd64] arg8: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [amd64] arg8: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [amd64] arg8: wrong argument size 2; expected $...-16 +main.go:1: [amd64] argarray: invalid MOVQ of x+0(FP); [2]testdata.S is 48-byte value +main.go:1: [amd64] argarray: invalid MOVQ of x_0_i+0(FP); int32 is 4-byte value +main.go:1: [amd64] argarray: invalid offset x_0_b+0(FP); expected x_0_b+4(FP) +main.go:1: [amd64] argarray: invalid offset x_0_s+16(FP); expected x_0_s+8(FP), x_0_s_base+8(FP), or x_0_s_len+16(FP) +main.go:1: [amd64] argarray: invalid offset x_1_s+40(FP); expected x_1_s+32(FP), x_1_s_base+32(FP), or x_1_s_len+40(FP) +main.go:1: [amd64] argarray: unknown variable foo; offset 25 is x_1_i+24(FP) +main.go:1: [amd64] argarray: wrong argument size 0; expected $...-48 +main.go:1: [amd64] argcomplex: invalid MOVSD of x+0(FP); complex64 is 8-byte value containing x_real+0(FP) and x_imag+4(FP) +main.go:1: [amd64] argcomplex: invalid MOVSD of x_imag+4(FP); imag(complex64) is 4-byte value +main.go:1: [amd64] argcomplex: invalid MOVSD of x_real+0(FP); real(complex64) is 4-byte value +main.go:1: [amd64] argcomplex: invalid MOVSD of y+8(FP); complex128 is 16-byte value containing y_real+8(FP) and y_imag+16(FP) +main.go:1: [amd64] argcomplex: invalid MOVSS of x+0(FP); complex64 is 8-byte value containing x_real+0(FP) and x_imag+4(FP) +main.go:1: [amd64] argcomplex: invalid MOVSS of y_imag+16(FP); imag(complex128) is 8-byte value +main.go:1: [amd64] argcomplex: invalid MOVSS of y_real+8(FP); real(complex128) is 8-byte value +main.go:1: [amd64] argcomplex: invalid offset x_imag+8(FP); expected x_imag+4(FP) +main.go:1: [amd64] argcomplex: invalid offset x_real+4(FP); expected x_real+0(FP) +main.go:1: [amd64] argcomplex: invalid offset y_imag+24(FP); expected y_imag+16(FP) +main.go:1: [amd64] argcomplex: invalid offset y_real+16(FP); expected y_real+8(FP) +main.go:1: [amd64] argcomplex: wrong argument size 0; expected $...-24 +main.go:1: [amd64] argiface: invalid MOVL of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [amd64] argiface: invalid MOVL of x_data+8(FP); interface data is 8-byte value +main.go:1: [amd64] argiface: invalid MOVL of x_type+0(FP); interface type is 8-byte value +main.go:1: [amd64] argiface: invalid MOVL of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [amd64] argiface: invalid MOVL of y_data+24(FP); interface data is 8-byte value +main.go:1: [amd64] argiface: invalid MOVL of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [amd64] argiface: invalid MOVW of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [amd64] argiface: invalid MOVW of x_data+8(FP); interface data is 8-byte value +main.go:1: [amd64] argiface: invalid MOVW of x_type+0(FP); interface type is 8-byte value +main.go:1: [amd64] argiface: invalid MOVW of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [amd64] argiface: invalid MOVW of y_data+24(FP); interface data is 8-byte value +main.go:1: [amd64] argiface: invalid MOVW of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [amd64] argiface: invalid offset x_data+0(FP); expected x_data+8(FP) +main.go:1: [amd64] argiface: invalid offset y_data+16(FP); expected y_data+24(FP) +main.go:1: [amd64] argiface: unknown variable x_itable; offset 0 is x_type+0(FP) +main.go:1: [amd64] argiface: unknown variable x_itable; offset 1 is x_type+0(FP) +main.go:1: [amd64] argiface: unknown variable y_type; offset 16 is y_itable+16(FP) +main.go:1: [amd64] argint: invalid MOVB of x+0(FP); int is 8-byte value +main.go:1: [amd64] argint: invalid MOVB of y+8(FP); uint is 8-byte value +main.go:1: [amd64] argint: invalid MOVL of x+0(FP); int is 8-byte value +main.go:1: [amd64] argint: invalid MOVL of y+8(FP); uint is 8-byte value +main.go:1: [amd64] argint: invalid MOVW of x+0(FP); int is 8-byte value +main.go:1: [amd64] argint: invalid MOVW of y+8(FP); uint is 8-byte value +main.go:1: [amd64] argint: invalid TESTB of x+0(FP); int is 8-byte value +main.go:1: [amd64] argint: invalid TESTB of y+8(FP); uint is 8-byte value +main.go:1: [amd64] argint: invalid TESTL of x+0(FP); int is 8-byte value +main.go:1: [amd64] argint: invalid TESTL of y+8(FP); uint is 8-byte value +main.go:1: [amd64] argint: invalid TESTW of x+0(FP); int is 8-byte value +main.go:1: [amd64] argint: invalid TESTW of y+8(FP); uint is 8-byte value +main.go:1: [amd64] argint: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [amd64] argint: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [amd64] argint: wrong argument size 2; expected $...-16 +main.go:1: [amd64] argptr: invalid MOVB of x+0(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid MOVB of y+8(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid MOVL of c+16(FP); chan int is 8-byte value +main.go:1: [amd64] argptr: invalid MOVL of f+32(FP); func() is 8-byte value +main.go:1: [amd64] argptr: invalid MOVL of m+24(FP); map[int]int is 8-byte value +main.go:1: [amd64] argptr: invalid MOVL of x+0(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid MOVL of y+8(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid MOVW of x+0(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid MOVW of y+8(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid TESTB of x+0(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid TESTB of y+8(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid TESTL of x+0(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid TESTL of y+8(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid TESTW of x+0(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid TESTW of y+8(FP); *byte is 8-byte value +main.go:1: [amd64] argptr: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [amd64] argptr: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [amd64] argptr: wrong argument size 2; expected $...-40 +main.go:1: [amd64] argslice: invalid MOVL of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [amd64] argslice: invalid MOVL of x_base+0(FP); slice base is 8-byte value +main.go:1: [amd64] argslice: invalid MOVL of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [amd64] argslice: invalid MOVL of x_len+8(FP); slice len is 8-byte value +main.go:1: [amd64] argslice: invalid MOVW of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [amd64] argslice: invalid MOVW of x_base+0(FP); slice base is 8-byte value +main.go:1: [amd64] argslice: invalid MOVW of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [amd64] argslice: invalid MOVW of x_len+8(FP); slice len is 8-byte value +main.go:1: [amd64] argslice: invalid offset x_cap+0(FP); expected x_cap+16(FP) +main.go:1: [amd64] argslice: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [amd64] argslice: invalid offset y+0(FP); expected y+24(FP), y_base+24(FP), y_len+32(FP), or y_cap+40(FP) +main.go:1: [amd64] argslice: invalid offset y_cap+16(FP); expected y_cap+40(FP) +main.go:1: [amd64] argslice: invalid offset y_len+8(FP); expected y_len+32(FP) +main.go:1: [amd64] argslice: wrong argument size 0; expected $...-48 +main.go:1: [amd64] argstring: invalid MOVL of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [amd64] argstring: invalid MOVL of x_base+0(FP); string base is 8-byte value +main.go:1: [amd64] argstring: invalid MOVL of x_len+8(FP); string len is 8-byte value +main.go:1: [amd64] argstring: invalid MOVW of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [amd64] argstring: invalid MOVW of x_base+0(FP); string base is 8-byte value +main.go:1: [amd64] argstring: invalid MOVW of x_len+8(FP); string len is 8-byte value +main.go:1: [amd64] argstring: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [amd64] argstring: invalid offset y+0(FP); expected y+16(FP), y_base+16(FP), or y_len+24(FP) +main.go:1: [amd64] argstring: invalid offset y_len+8(FP); expected y_len+24(FP) +main.go:1: [amd64] argstring: wrong argument size 0; expected $...-32 +main.go:1: [amd64] argstruct: invalid MOVQ of x+0(FP); testdata.S is 24-byte value +main.go:1: [amd64] argstruct: invalid MOVQ of x_i+0(FP); int32 is 4-byte value +main.go:1: [amd64] argstruct: invalid offset x_b+0(FP); expected x_b+4(FP) +main.go:1: [amd64] argstruct: invalid offset x_s+16(FP); expected x_s+8(FP), x_s_base+8(FP), or x_s_len+16(FP) +main.go:1: [amd64] argstruct: wrong argument size 0; expected $...-24 +main.go:1: [amd64] returnbyte: invalid MOVL of ret+8(FP); byte is 1-byte value +main.go:1: [amd64] returnbyte: invalid MOVQ of ret+8(FP); byte is 1-byte value +main.go:1: [amd64] returnbyte: invalid MOVW of ret+8(FP); byte is 1-byte value +main.go:1: [amd64] returnbyte: invalid offset ret+7(FP); expected ret+8(FP) +main.go:1: [amd64] returnint: invalid MOVB of ret+0(FP); int is 8-byte value +main.go:1: [amd64] returnint: invalid MOVL of ret+0(FP); int is 8-byte value +main.go:1: [amd64] returnint: invalid MOVW of ret+0(FP); int is 8-byte value +main.go:1: [amd64] returnint: invalid offset ret+1(FP); expected ret+0(FP) +main.go:1: [amd64] returnint: unknown variable r; offset 0 is ret+0(FP) +main.go:1: [amd64] returnintmissing: RET without writing to 8-byte ret+0(FP) +main.go:1: [amd64] returnnamed: invalid MOVL of r1+8(FP); int is 8-byte value +main.go:1: [arm] arg1: 8(R13) should be x+0(FP) +main.go:1: [arm] arg1: 9(R13) should be y+1(FP) +main.go:1: [arm] arg1: invalid MOVH of x+0(FP); int8 is 1-byte value +main.go:1: [arm] arg1: invalid MOVH of y+1(FP); uint8 is 1-byte value +main.go:1: [arm] arg1: invalid MOVW of x+0(FP); int8 is 1-byte value +main.go:1: [arm] arg1: invalid MOVW of y+1(FP); uint8 is 1-byte value +main.go:1: [arm] arg1: invalid offset x+1(FP); expected x+0(FP) +main.go:1: [arm] arg1: invalid offset y+2(FP); expected y+1(FP) +main.go:1: [arm] arg1: use of 10(R13) points beyond argument frame +main.go:1: [arm] arg2: invalid MOVB of x+0(FP); int16 is 2-byte value +main.go:1: [arm] arg2: invalid MOVB of y+2(FP); uint16 is 2-byte value +main.go:1: [arm] arg2: invalid MOVW of x+0(FP); int16 is 2-byte value +main.go:1: [arm] arg2: invalid MOVW of y+2(FP); uint16 is 2-byte value +main.go:1: [arm] arg2: invalid offset x+2(FP); expected x+0(FP) +main.go:1: [arm] arg2: invalid offset y+0(FP); expected y+2(FP) +main.go:1: [arm] arg4: invalid MOVB of x+0(FP); int32 is 4-byte value +main.go:1: [arm] arg4: invalid MOVB of y+4(FP); uint32 is 4-byte value +main.go:1: [arm] arg4: invalid MOVH of x+0(FP); int32 is 4-byte value +main.go:1: [arm] arg4: invalid MOVH of y+4(FP); uint32 is 4-byte value +main.go:1: [arm] arg4: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [arm] arg4: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [arm] arg4: wrong argument size 2; expected $...-8 +main.go:1: [arm] arg8: invalid MOVB of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [arm] arg8: invalid MOVB of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [arm] arg8: invalid MOVH of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [arm] arg8: invalid MOVH of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [arm] arg8: invalid MOVW of x+0(FP); int64 is 8-byte value containing x_lo+0(FP) and x_hi+4(FP) +main.go:1: [arm] arg8: invalid MOVW of y+8(FP); uint64 is 8-byte value containing y_lo+8(FP) and y_hi+12(FP) +main.go:1: [arm] arg8: invalid offset x+8(FP); expected x+0(FP), x_lo+0(FP), or x_hi+4(FP) +main.go:1: [arm] arg8: invalid offset y+2(FP); expected y+8(FP), y_lo+8(FP), or y_hi+12(FP) +main.go:1: [arm] arg8: wrong argument size 2; expected $...-16 +main.go:1: [arm] argiface: invalid MOVH of x+0(FP); interface type is 4-byte value containing x_type+0(FP) and x_data+4(FP) +main.go:1: [arm] argiface: invalid MOVH of x_data+4(FP); interface data is 4-byte value +main.go:1: [arm] argiface: invalid MOVH of x_type+0(FP); interface type is 4-byte value +main.go:1: [arm] argiface: invalid MOVH of y+8(FP); interface itable is 4-byte value containing y_itable+8(FP) and y_data+12(FP) +main.go:1: [arm] argiface: invalid MOVH of y_data+12(FP); interface data is 4-byte value +main.go:1: [arm] argiface: invalid MOVH of y_itable+8(FP); interface itable is 4-byte value +main.go:1: [arm] argiface: invalid offset x_data+0(FP); expected x_data+4(FP) +main.go:1: [arm] argiface: invalid offset y_data+8(FP); expected y_data+12(FP) +main.go:1: [arm] argiface: unknown variable x_itable; offset 0 is x_type+0(FP) +main.go:1: [arm] argiface: unknown variable x_itable; offset 1 is x_type+0(FP) +main.go:1: [arm] argiface: unknown variable y_type; offset 8 is y_itable+8(FP) +main.go:1: [arm] argint: invalid MOVB of x+0(FP); int is 4-byte value +main.go:1: [arm] argint: invalid MOVB of y+4(FP); uint is 4-byte value +main.go:1: [arm] argint: invalid MOVH of x+0(FP); int is 4-byte value +main.go:1: [arm] argint: invalid MOVH of y+4(FP); uint is 4-byte value +main.go:1: [arm] argint: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [arm] argint: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [arm] argint: wrong argument size 2; expected $...-8 +main.go:1: [arm] argptr: invalid MOVB of x+0(FP); *byte is 4-byte value +main.go:1: [arm] argptr: invalid MOVB of y+4(FP); *byte is 4-byte value +main.go:1: [arm] argptr: invalid MOVH of c+8(FP); chan int is 4-byte value +main.go:1: [arm] argptr: invalid MOVH of f+16(FP); func() is 4-byte value +main.go:1: [arm] argptr: invalid MOVH of m+12(FP); map[int]int is 4-byte value +main.go:1: [arm] argptr: invalid MOVH of x+0(FP); *byte is 4-byte value +main.go:1: [arm] argptr: invalid MOVH of y+4(FP); *byte is 4-byte value +main.go:1: [arm] argptr: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [arm] argptr: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [arm] argptr: wrong argument size 2; expected $...-20 +main.go:1: [arm] argslice: invalid MOVH of x+0(FP); slice base is 4-byte value containing x_base+0(FP), x_len+4(FP), and x_cap+8(FP) +main.go:1: [arm] argslice: invalid MOVH of x_base+0(FP); slice base is 4-byte value +main.go:1: [arm] argslice: invalid MOVH of x_cap+8(FP); slice cap is 4-byte value +main.go:1: [arm] argslice: invalid MOVH of x_len+4(FP); slice len is 4-byte value +main.go:1: [arm] argslice: invalid offset x_cap+0(FP); expected x_cap+8(FP) +main.go:1: [arm] argslice: invalid offset x_len+0(FP); expected x_len+4(FP) +main.go:1: [arm] argslice: invalid offset y+0(FP); expected y+12(FP), y_base+12(FP), y_len+16(FP), or y_cap+20(FP) +main.go:1: [arm] argslice: invalid offset y_cap+8(FP); expected y_cap+20(FP) +main.go:1: [arm] argslice: invalid offset y_len+4(FP); expected y_len+16(FP) +main.go:1: [arm] argslice: wrong argument size 0; expected $...-24 +main.go:1: [arm] argstring: invalid MOVH of x+0(FP); string base is 4-byte value containing x_base+0(FP) and x_len+4(FP) +main.go:1: [arm] argstring: invalid MOVH of x_base+0(FP); string base is 4-byte value +main.go:1: [arm] argstring: invalid MOVH of x_len+4(FP); string len is 4-byte value +main.go:1: [arm] argstring: invalid offset x_len+0(FP); expected x_len+4(FP) +main.go:1: [arm] argstring: invalid offset y+0(FP); expected y+8(FP), y_base+8(FP), or y_len+12(FP) +main.go:1: [arm] argstring: invalid offset y_len+4(FP); expected y_len+12(FP) +main.go:1: [arm] argstring: wrong argument size 0; expected $...-16 +main.go:1: [arm] noframe1: use of 12(R13) points beyond argument frame +main.go:1: [arm] noframe2: 8(R13) should be x+0(FP) +main.go:1: [arm] noframe2: use of 12(R13) points beyond argument frame +main.go:1: [arm] returnbyte: invalid MOVH of ret+4(FP); byte is 1-byte value +main.go:1: [arm] returnbyte: invalid MOVW of ret+4(FP); byte is 1-byte value +main.go:1: [arm] returnbyte: invalid offset ret+3(FP); expected ret+4(FP) +main.go:1: [arm] returnint: invalid MOVB of ret+0(FP); int is 4-byte value +main.go:1: [arm] returnint: invalid MOVH of ret+0(FP); int is 4-byte value +main.go:1: [arm] returnint: invalid offset ret+1(FP); expected ret+0(FP) +main.go:1: [arm] returnint: unknown variable r; offset 0 is ret+0(FP) +main.go:1: [arm] returnintmissing: RET without writing to 4-byte ret+0(FP) +main.go:1: [arm] returnnamed: invalid MOVB of r1+4(FP); int is 4-byte value +main.go:1: [mips64] arg1: 16(R29) should be x+0(FP) +main.go:1: [mips64] arg1: 17(R29) should be y+1(FP) +main.go:1: [mips64] arg1: invalid MOVH of x+0(FP); int8 is 1-byte value +main.go:1: [mips64] arg1: invalid MOVHU of y+1(FP); uint8 is 1-byte value +main.go:1: [mips64] arg1: invalid MOVV of x+0(FP); int8 is 1-byte value +main.go:1: [mips64] arg1: invalid MOVV of y+1(FP); uint8 is 1-byte value +main.go:1: [mips64] arg1: invalid MOVW of x+0(FP); int8 is 1-byte value +main.go:1: [mips64] arg1: invalid MOVWU of y+1(FP); uint8 is 1-byte value +main.go:1: [mips64] arg1: invalid offset x+1(FP); expected x+0(FP) +main.go:1: [mips64] arg1: invalid offset y+2(FP); expected y+1(FP) +main.go:1: [mips64] arg1: use of 18(R29) points beyond argument frame +main.go:1: [mips64] arg2: invalid MOVB of y+2(FP); uint16 is 2-byte value +main.go:1: [mips64] arg2: invalid MOVBU of x+0(FP); int16 is 2-byte value +main.go:1: [mips64] arg2: invalid MOVV of x+0(FP); int16 is 2-byte value +main.go:1: [mips64] arg2: invalid MOVV of y+2(FP); uint16 is 2-byte value +main.go:1: [mips64] arg2: invalid MOVW of y+2(FP); uint16 is 2-byte value +main.go:1: [mips64] arg2: invalid MOVWU of x+0(FP); int16 is 2-byte value +main.go:1: [mips64] arg2: invalid offset x+2(FP); expected x+0(FP) +main.go:1: [mips64] arg2: invalid offset y+0(FP); expected y+2(FP) +main.go:1: [mips64] arg4: invalid MOVB of x+0(FP); int32 is 4-byte value +main.go:1: [mips64] arg4: invalid MOVB of y+4(FP); uint32 is 4-byte value +main.go:1: [mips64] arg4: invalid MOVH of x+0(FP); int32 is 4-byte value +main.go:1: [mips64] arg4: invalid MOVH of y+4(FP); uint32 is 4-byte value +main.go:1: [mips64] arg4: invalid MOVV of x+0(FP); int32 is 4-byte value +main.go:1: [mips64] arg4: invalid MOVV of y+4(FP); uint32 is 4-byte value +main.go:1: [mips64] arg4: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [mips64] arg4: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [mips64] arg4: wrong argument size 2; expected $...-8 +main.go:1: [mips64] arg8: invalid MOVB of x+0(FP); int64 is 8-byte value +main.go:1: [mips64] arg8: invalid MOVB of y+8(FP); uint64 is 8-byte value +main.go:1: [mips64] arg8: invalid MOVH of x+0(FP); int64 is 8-byte value +main.go:1: [mips64] arg8: invalid MOVH of y+8(FP); uint64 is 8-byte value +main.go:1: [mips64] arg8: invalid MOVW of x+0(FP); int64 is 8-byte value +main.go:1: [mips64] arg8: invalid MOVW of y+8(FP); uint64 is 8-byte value +main.go:1: [mips64] arg8: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [mips64] arg8: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [mips64] arg8: wrong argument size 2; expected $...-16 +main.go:1: [mips64] argiface: invalid MOVH of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [mips64] argiface: invalid MOVH of x_data+8(FP); interface data is 8-byte value +main.go:1: [mips64] argiface: invalid MOVH of x_type+0(FP); interface type is 8-byte value +main.go:1: [mips64] argiface: invalid MOVH of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [mips64] argiface: invalid MOVH of y_data+24(FP); interface data is 8-byte value +main.go:1: [mips64] argiface: invalid MOVH of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [mips64] argiface: invalid MOVW of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [mips64] argiface: invalid MOVW of x_data+8(FP); interface data is 8-byte value +main.go:1: [mips64] argiface: invalid MOVW of x_type+0(FP); interface type is 8-byte value +main.go:1: [mips64] argiface: invalid MOVW of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [mips64] argiface: invalid MOVW of y_data+24(FP); interface data is 8-byte value +main.go:1: [mips64] argiface: invalid MOVW of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [mips64] argiface: invalid offset x_data+0(FP); expected x_data+8(FP) +main.go:1: [mips64] argiface: invalid offset y_data+16(FP); expected y_data+24(FP) +main.go:1: [mips64] argiface: unknown variable x_itable; offset 0 is x_type+0(FP) +main.go:1: [mips64] argiface: unknown variable x_itable; offset 1 is x_type+0(FP) +main.go:1: [mips64] argiface: unknown variable y_type; offset 16 is y_itable+16(FP) +main.go:1: [mips64] argint: invalid MOVB of x+0(FP); int is 8-byte value +main.go:1: [mips64] argint: invalid MOVB of y+8(FP); uint is 8-byte value +main.go:1: [mips64] argint: invalid MOVH of x+0(FP); int is 8-byte value +main.go:1: [mips64] argint: invalid MOVH of y+8(FP); uint is 8-byte value +main.go:1: [mips64] argint: invalid MOVW of x+0(FP); int is 8-byte value +main.go:1: [mips64] argint: invalid MOVW of y+8(FP); uint is 8-byte value +main.go:1: [mips64] argint: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [mips64] argint: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [mips64] argint: wrong argument size 2; expected $...-16 +main.go:1: [mips64] argptr: invalid MOVB of x+0(FP); *byte is 8-byte value +main.go:1: [mips64] argptr: invalid MOVB of y+8(FP); *byte is 8-byte value +main.go:1: [mips64] argptr: invalid MOVH of x+0(FP); *byte is 8-byte value +main.go:1: [mips64] argptr: invalid MOVH of y+8(FP); *byte is 8-byte value +main.go:1: [mips64] argptr: invalid MOVW of c+16(FP); chan int is 8-byte value +main.go:1: [mips64] argptr: invalid MOVW of f+32(FP); func() is 8-byte value +main.go:1: [mips64] argptr: invalid MOVW of m+24(FP); map[int]int is 8-byte value +main.go:1: [mips64] argptr: invalid MOVW of x+0(FP); *byte is 8-byte value +main.go:1: [mips64] argptr: invalid MOVW of y+8(FP); *byte is 8-byte value +main.go:1: [mips64] argptr: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [mips64] argptr: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [mips64] argptr: wrong argument size 2; expected $...-40 +main.go:1: [mips64] argslice: invalid MOVH of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [mips64] argslice: invalid MOVH of x_base+0(FP); slice base is 8-byte value +main.go:1: [mips64] argslice: invalid MOVH of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [mips64] argslice: invalid MOVH of x_len+8(FP); slice len is 8-byte value +main.go:1: [mips64] argslice: invalid MOVW of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [mips64] argslice: invalid MOVW of x_base+0(FP); slice base is 8-byte value +main.go:1: [mips64] argslice: invalid MOVW of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [mips64] argslice: invalid MOVW of x_len+8(FP); slice len is 8-byte value +main.go:1: [mips64] argslice: invalid offset x_cap+0(FP); expected x_cap+16(FP) +main.go:1: [mips64] argslice: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [mips64] argslice: invalid offset y+0(FP); expected y+24(FP), y_base+24(FP), y_len+32(FP), or y_cap+40(FP) +main.go:1: [mips64] argslice: invalid offset y_cap+16(FP); expected y_cap+40(FP) +main.go:1: [mips64] argslice: invalid offset y_len+8(FP); expected y_len+32(FP) +main.go:1: [mips64] argslice: wrong argument size 0; expected $...-48 +main.go:1: [mips64] argstring: invalid MOVH of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [mips64] argstring: invalid MOVH of x_base+0(FP); string base is 8-byte value +main.go:1: [mips64] argstring: invalid MOVH of x_len+8(FP); string len is 8-byte value +main.go:1: [mips64] argstring: invalid MOVW of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [mips64] argstring: invalid MOVW of x_base+0(FP); string base is 8-byte value +main.go:1: [mips64] argstring: invalid MOVW of x_len+8(FP); string len is 8-byte value +main.go:1: [mips64] argstring: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [mips64] argstring: invalid offset y+0(FP); expected y+16(FP), y_base+16(FP), or y_len+24(FP) +main.go:1: [mips64] argstring: invalid offset y_len+8(FP); expected y_len+24(FP) +main.go:1: [mips64] argstring: wrong argument size 0; expected $...-32 +main.go:1: [mips64] returnbyte: invalid MOVH of ret+8(FP); byte is 1-byte value +main.go:1: [mips64] returnbyte: invalid MOVV of ret+8(FP); byte is 1-byte value +main.go:1: [mips64] returnbyte: invalid MOVW of ret+8(FP); byte is 1-byte value +main.go:1: [mips64] returnbyte: invalid offset ret+7(FP); expected ret+8(FP) +main.go:1: [mips64] returnint: invalid MOVB of ret+0(FP); int is 8-byte value +main.go:1: [mips64] returnint: invalid MOVH of ret+0(FP); int is 8-byte value +main.go:1: [mips64] returnint: invalid MOVW of ret+0(FP); int is 8-byte value +main.go:1: [mips64] returnint: invalid offset ret+1(FP); expected ret+0(FP) +main.go:1: [mips64] returnint: unknown variable r; offset 0 is ret+0(FP) +main.go:1: [mips64] returnintmissing: RET without writing to 8-byte ret+0(FP) +main.go:1: [mips64] returnnamed: invalid MOVW of r1+8(FP); int is 8-byte value +main.go:1: [ppc64] arg1: 16(R1) should be x+0(FP) +main.go:1: [ppc64] arg1: 17(R1) should be y+1(FP) +main.go:1: [ppc64] arg1: invalid MOVD of x+0(FP); int8 is 1-byte value +main.go:1: [ppc64] arg1: invalid MOVD of y+1(FP); uint8 is 1-byte value +main.go:1: [ppc64] arg1: invalid MOVH of x+0(FP); int8 is 1-byte value +main.go:1: [ppc64] arg1: invalid MOVHZ of y+1(FP); uint8 is 1-byte value +main.go:1: [ppc64] arg1: invalid MOVW of x+0(FP); int8 is 1-byte value +main.go:1: [ppc64] arg1: invalid MOVWZ of y+1(FP); uint8 is 1-byte value +main.go:1: [ppc64] arg1: invalid offset x+1(FP); expected x+0(FP) +main.go:1: [ppc64] arg1: invalid offset y+2(FP); expected y+1(FP) +main.go:1: [ppc64] arg1: use of 18(R1) points beyond argument frame +main.go:1: [ppc64] arg2: invalid MOVB of y+2(FP); uint16 is 2-byte value +main.go:1: [ppc64] arg2: invalid MOVBZ of x+0(FP); int16 is 2-byte value +main.go:1: [ppc64] arg2: invalid MOVD of x+0(FP); int16 is 2-byte value +main.go:1: [ppc64] arg2: invalid MOVD of y+2(FP); uint16 is 2-byte value +main.go:1: [ppc64] arg2: invalid MOVW of y+2(FP); uint16 is 2-byte value +main.go:1: [ppc64] arg2: invalid MOVWZ of x+0(FP); int16 is 2-byte value +main.go:1: [ppc64] arg2: invalid offset x+2(FP); expected x+0(FP) +main.go:1: [ppc64] arg2: invalid offset y+0(FP); expected y+2(FP) +main.go:1: [ppc64] arg4: invalid MOVB of x+0(FP); int32 is 4-byte value +main.go:1: [ppc64] arg4: invalid MOVB of y+4(FP); uint32 is 4-byte value +main.go:1: [ppc64] arg4: invalid MOVD of x+0(FP); int32 is 4-byte value +main.go:1: [ppc64] arg4: invalid MOVD of y+4(FP); uint32 is 4-byte value +main.go:1: [ppc64] arg4: invalid MOVH of x+0(FP); int32 is 4-byte value +main.go:1: [ppc64] arg4: invalid MOVH of y+4(FP); uint32 is 4-byte value +main.go:1: [ppc64] arg4: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [ppc64] arg4: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [ppc64] arg4: wrong argument size 2; expected $...-8 +main.go:1: [ppc64] arg8: invalid MOVB of x+0(FP); int64 is 8-byte value +main.go:1: [ppc64] arg8: invalid MOVB of y+8(FP); uint64 is 8-byte value +main.go:1: [ppc64] arg8: invalid MOVH of x+0(FP); int64 is 8-byte value +main.go:1: [ppc64] arg8: invalid MOVH of y+8(FP); uint64 is 8-byte value +main.go:1: [ppc64] arg8: invalid MOVW of x+0(FP); int64 is 8-byte value +main.go:1: [ppc64] arg8: invalid MOVW of y+8(FP); uint64 is 8-byte value +main.go:1: [ppc64] arg8: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [ppc64] arg8: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [ppc64] arg8: wrong argument size 2; expected $...-16 +main.go:1: [ppc64] argiface: invalid MOVH of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [ppc64] argiface: invalid MOVH of x_data+8(FP); interface data is 8-byte value +main.go:1: [ppc64] argiface: invalid MOVH of x_type+0(FP); interface type is 8-byte value +main.go:1: [ppc64] argiface: invalid MOVH of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [ppc64] argiface: invalid MOVH of y_data+24(FP); interface data is 8-byte value +main.go:1: [ppc64] argiface: invalid MOVH of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [ppc64] argiface: invalid MOVW of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [ppc64] argiface: invalid MOVW of x_data+8(FP); interface data is 8-byte value +main.go:1: [ppc64] argiface: invalid MOVW of x_type+0(FP); interface type is 8-byte value +main.go:1: [ppc64] argiface: invalid MOVW of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [ppc64] argiface: invalid MOVW of y_data+24(FP); interface data is 8-byte value +main.go:1: [ppc64] argiface: invalid MOVW of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [ppc64] argiface: invalid offset x_data+0(FP); expected x_data+8(FP) +main.go:1: [ppc64] argiface: invalid offset y_data+16(FP); expected y_data+24(FP) +main.go:1: [ppc64] argiface: unknown variable x_itable; offset 0 is x_type+0(FP) +main.go:1: [ppc64] argiface: unknown variable x_itable; offset 1 is x_type+0(FP) +main.go:1: [ppc64] argiface: unknown variable y_type; offset 16 is y_itable+16(FP) +main.go:1: [ppc64] argint: invalid MOVB of x+0(FP); int is 8-byte value +main.go:1: [ppc64] argint: invalid MOVB of y+8(FP); uint is 8-byte value +main.go:1: [ppc64] argint: invalid MOVH of x+0(FP); int is 8-byte value +main.go:1: [ppc64] argint: invalid MOVH of y+8(FP); uint is 8-byte value +main.go:1: [ppc64] argint: invalid MOVW of x+0(FP); int is 8-byte value +main.go:1: [ppc64] argint: invalid MOVW of y+8(FP); uint is 8-byte value +main.go:1: [ppc64] argint: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [ppc64] argint: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [ppc64] argint: wrong argument size 2; expected $...-16 +main.go:1: [ppc64] argptr: invalid MOVB of x+0(FP); *byte is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVB of y+8(FP); *byte is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVH of x+0(FP); *byte is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVH of y+8(FP); *byte is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVW of c+16(FP); chan int is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVW of f+32(FP); func() is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVW of m+24(FP); map[int]int is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVW of x+0(FP); *byte is 8-byte value +main.go:1: [ppc64] argptr: invalid MOVW of y+8(FP); *byte is 8-byte value +main.go:1: [ppc64] argptr: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [ppc64] argptr: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [ppc64] argptr: wrong argument size 2; expected $...-40 +main.go:1: [ppc64] argslice: invalid MOVH of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [ppc64] argslice: invalid MOVH of x_base+0(FP); slice base is 8-byte value +main.go:1: [ppc64] argslice: invalid MOVH of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [ppc64] argslice: invalid MOVH of x_len+8(FP); slice len is 8-byte value +main.go:1: [ppc64] argslice: invalid MOVW of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [ppc64] argslice: invalid MOVW of x_base+0(FP); slice base is 8-byte value +main.go:1: [ppc64] argslice: invalid MOVW of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [ppc64] argslice: invalid MOVW of x_len+8(FP); slice len is 8-byte value +main.go:1: [ppc64] argslice: invalid offset x_cap+0(FP); expected x_cap+16(FP) +main.go:1: [ppc64] argslice: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [ppc64] argslice: invalid offset y+0(FP); expected y+24(FP), y_base+24(FP), y_len+32(FP), or y_cap+40(FP) +main.go:1: [ppc64] argslice: invalid offset y_cap+16(FP); expected y_cap+40(FP) +main.go:1: [ppc64] argslice: invalid offset y_len+8(FP); expected y_len+32(FP) +main.go:1: [ppc64] argslice: wrong argument size 0; expected $...-48 +main.go:1: [ppc64] argstring: invalid MOVH of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [ppc64] argstring: invalid MOVH of x_base+0(FP); string base is 8-byte value +main.go:1: [ppc64] argstring: invalid MOVH of x_len+8(FP); string len is 8-byte value +main.go:1: [ppc64] argstring: invalid MOVW of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [ppc64] argstring: invalid MOVW of x_base+0(FP); string base is 8-byte value +main.go:1: [ppc64] argstring: invalid MOVW of x_len+8(FP); string len is 8-byte value +main.go:1: [ppc64] argstring: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [ppc64] argstring: invalid offset y+0(FP); expected y+16(FP), y_base+16(FP), or y_len+24(FP) +main.go:1: [ppc64] argstring: invalid offset y_len+8(FP); expected y_len+24(FP) +main.go:1: [ppc64] argstring: wrong argument size 0; expected $...-32 +main.go:1: [ppc64] returnbyte: invalid MOVD of ret+8(FP); byte is 1-byte value +main.go:1: [ppc64] returnbyte: invalid MOVH of ret+8(FP); byte is 1-byte value +main.go:1: [ppc64] returnbyte: invalid MOVW of ret+8(FP); byte is 1-byte value +main.go:1: [ppc64] returnbyte: invalid offset ret+7(FP); expected ret+8(FP) +main.go:1: [ppc64] returnint: invalid MOVB of ret+0(FP); int is 8-byte value +main.go:1: [ppc64] returnint: invalid MOVH of ret+0(FP); int is 8-byte value +main.go:1: [ppc64] returnint: invalid MOVW of ret+0(FP); int is 8-byte value +main.go:1: [ppc64] returnint: invalid offset ret+1(FP); expected ret+0(FP) +main.go:1: [ppc64] returnint: unknown variable r; offset 0 is ret+0(FP) +main.go:1: [ppc64] returnintmissing: RET without writing to 8-byte ret+0(FP) +main.go:1: [ppc64] returnnamed: invalid MOVW of r1+8(FP); int is 8-byte value +main.go:1: [s390x] arg1: 16(R15) should be x+0(FP) +main.go:1: [s390x] arg1: 17(R15) should be y+1(FP) +main.go:1: [s390x] arg1: invalid MOVD of x+0(FP); int8 is 1-byte value +main.go:1: [s390x] arg1: invalid MOVD of y+1(FP); uint8 is 1-byte value +main.go:1: [s390x] arg1: invalid MOVH of x+0(FP); int8 is 1-byte value +main.go:1: [s390x] arg1: invalid MOVHZ of y+1(FP); uint8 is 1-byte value +main.go:1: [s390x] arg1: invalid MOVW of x+0(FP); int8 is 1-byte value +main.go:1: [s390x] arg1: invalid MOVWZ of y+1(FP); uint8 is 1-byte value +main.go:1: [s390x] arg1: invalid offset x+1(FP); expected x+0(FP) +main.go:1: [s390x] arg1: invalid offset y+2(FP); expected y+1(FP) +main.go:1: [s390x] arg1: use of 18(R15) points beyond argument frame +main.go:1: [s390x] arg2: invalid MOVB of y+2(FP); uint16 is 2-byte value +main.go:1: [s390x] arg2: invalid MOVBZ of x+0(FP); int16 is 2-byte value +main.go:1: [s390x] arg2: invalid MOVD of x+0(FP); int16 is 2-byte value +main.go:1: [s390x] arg2: invalid MOVD of y+2(FP); uint16 is 2-byte value +main.go:1: [s390x] arg2: invalid MOVW of y+2(FP); uint16 is 2-byte value +main.go:1: [s390x] arg2: invalid MOVWZ of x+0(FP); int16 is 2-byte value +main.go:1: [s390x] arg2: invalid offset x+2(FP); expected x+0(FP) +main.go:1: [s390x] arg2: invalid offset y+0(FP); expected y+2(FP) +main.go:1: [s390x] arg4: invalid MOVB of x+0(FP); int32 is 4-byte value +main.go:1: [s390x] arg4: invalid MOVB of y+4(FP); uint32 is 4-byte value +main.go:1: [s390x] arg4: invalid MOVD of x+0(FP); int32 is 4-byte value +main.go:1: [s390x] arg4: invalid MOVD of y+4(FP); uint32 is 4-byte value +main.go:1: [s390x] arg4: invalid MOVH of x+0(FP); int32 is 4-byte value +main.go:1: [s390x] arg4: invalid MOVH of y+4(FP); uint32 is 4-byte value +main.go:1: [s390x] arg4: invalid offset x+4(FP); expected x+0(FP) +main.go:1: [s390x] arg4: invalid offset y+2(FP); expected y+4(FP) +main.go:1: [s390x] arg4: wrong argument size 2; expected $...-8 +main.go:1: [s390x] arg8: invalid MOVB of x+0(FP); int64 is 8-byte value +main.go:1: [s390x] arg8: invalid MOVB of y+8(FP); uint64 is 8-byte value +main.go:1: [s390x] arg8: invalid MOVH of x+0(FP); int64 is 8-byte value +main.go:1: [s390x] arg8: invalid MOVH of y+8(FP); uint64 is 8-byte value +main.go:1: [s390x] arg8: invalid MOVW of x+0(FP); int64 is 8-byte value +main.go:1: [s390x] arg8: invalid MOVW of y+8(FP); uint64 is 8-byte value +main.go:1: [s390x] arg8: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [s390x] arg8: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [s390x] arg8: wrong argument size 2; expected $...-16 +main.go:1: [s390x] argiface: invalid MOVH of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [s390x] argiface: invalid MOVH of x_data+8(FP); interface data is 8-byte value +main.go:1: [s390x] argiface: invalid MOVH of x_type+0(FP); interface type is 8-byte value +main.go:1: [s390x] argiface: invalid MOVH of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [s390x] argiface: invalid MOVH of y_data+24(FP); interface data is 8-byte value +main.go:1: [s390x] argiface: invalid MOVH of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [s390x] argiface: invalid MOVW of x+0(FP); interface type is 8-byte value containing x_type+0(FP) and x_data+8(FP) +main.go:1: [s390x] argiface: invalid MOVW of x_data+8(FP); interface data is 8-byte value +main.go:1: [s390x] argiface: invalid MOVW of x_type+0(FP); interface type is 8-byte value +main.go:1: [s390x] argiface: invalid MOVW of y+16(FP); interface itable is 8-byte value containing y_itable+16(FP) and y_data+24(FP) +main.go:1: [s390x] argiface: invalid MOVW of y_data+24(FP); interface data is 8-byte value +main.go:1: [s390x] argiface: invalid MOVW of y_itable+16(FP); interface itable is 8-byte value +main.go:1: [s390x] argiface: invalid offset x_data+0(FP); expected x_data+8(FP) +main.go:1: [s390x] argiface: invalid offset y_data+16(FP); expected y_data+24(FP) +main.go:1: [s390x] argiface: unknown variable x_itable; offset 0 is x_type+0(FP) +main.go:1: [s390x] argiface: unknown variable x_itable; offset 1 is x_type+0(FP) +main.go:1: [s390x] argiface: unknown variable y_type; offset 16 is y_itable+16(FP) +main.go:1: [s390x] argint: invalid MOVB of x+0(FP); int is 8-byte value +main.go:1: [s390x] argint: invalid MOVB of y+8(FP); uint is 8-byte value +main.go:1: [s390x] argint: invalid MOVH of x+0(FP); int is 8-byte value +main.go:1: [s390x] argint: invalid MOVH of y+8(FP); uint is 8-byte value +main.go:1: [s390x] argint: invalid MOVW of x+0(FP); int is 8-byte value +main.go:1: [s390x] argint: invalid MOVW of y+8(FP); uint is 8-byte value +main.go:1: [s390x] argint: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [s390x] argint: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [s390x] argint: wrong argument size 2; expected $...-16 +main.go:1: [s390x] argptr: invalid MOVB of x+0(FP); *byte is 8-byte value +main.go:1: [s390x] argptr: invalid MOVB of y+8(FP); *byte is 8-byte value +main.go:1: [s390x] argptr: invalid MOVH of x+0(FP); *byte is 8-byte value +main.go:1: [s390x] argptr: invalid MOVH of y+8(FP); *byte is 8-byte value +main.go:1: [s390x] argptr: invalid MOVW of c+16(FP); chan int is 8-byte value +main.go:1: [s390x] argptr: invalid MOVW of f+32(FP); func() is 8-byte value +main.go:1: [s390x] argptr: invalid MOVW of m+24(FP); map[int]int is 8-byte value +main.go:1: [s390x] argptr: invalid MOVW of x+0(FP); *byte is 8-byte value +main.go:1: [s390x] argptr: invalid MOVW of y+8(FP); *byte is 8-byte value +main.go:1: [s390x] argptr: invalid offset x+8(FP); expected x+0(FP) +main.go:1: [s390x] argptr: invalid offset y+2(FP); expected y+8(FP) +main.go:1: [s390x] argptr: wrong argument size 2; expected $...-40 +main.go:1: [s390x] argslice: invalid MOVH of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [s390x] argslice: invalid MOVH of x_base+0(FP); slice base is 8-byte value +main.go:1: [s390x] argslice: invalid MOVH of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [s390x] argslice: invalid MOVH of x_len+8(FP); slice len is 8-byte value +main.go:1: [s390x] argslice: invalid MOVW of x+0(FP); slice base is 8-byte value containing x_base+0(FP), x_len+8(FP), and x_cap+16(FP) +main.go:1: [s390x] argslice: invalid MOVW of x_base+0(FP); slice base is 8-byte value +main.go:1: [s390x] argslice: invalid MOVW of x_cap+16(FP); slice cap is 8-byte value +main.go:1: [s390x] argslice: invalid MOVW of x_len+8(FP); slice len is 8-byte value +main.go:1: [s390x] argslice: invalid offset x_cap+0(FP); expected x_cap+16(FP) +main.go:1: [s390x] argslice: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [s390x] argslice: invalid offset y+0(FP); expected y+24(FP), y_base+24(FP), y_len+32(FP), or y_cap+40(FP) +main.go:1: [s390x] argslice: invalid offset y_cap+16(FP); expected y_cap+40(FP) +main.go:1: [s390x] argslice: invalid offset y_len+8(FP); expected y_len+32(FP) +main.go:1: [s390x] argslice: wrong argument size 0; expected $...-48 +main.go:1: [s390x] argstring: invalid MOVH of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [s390x] argstring: invalid MOVH of x_base+0(FP); string base is 8-byte value +main.go:1: [s390x] argstring: invalid MOVH of x_len+8(FP); string len is 8-byte value +main.go:1: [s390x] argstring: invalid MOVW of x+0(FP); string base is 8-byte value containing x_base+0(FP) and x_len+8(FP) +main.go:1: [s390x] argstring: invalid MOVW of x_base+0(FP); string base is 8-byte value +main.go:1: [s390x] argstring: invalid MOVW of x_len+8(FP); string len is 8-byte value +main.go:1: [s390x] argstring: invalid offset x_len+0(FP); expected x_len+8(FP) +main.go:1: [s390x] argstring: invalid offset y+0(FP); expected y+16(FP), y_base+16(FP), or y_len+24(FP) +main.go:1: [s390x] argstring: invalid offset y_len+8(FP); expected y_len+24(FP) +main.go:1: [s390x] argstring: wrong argument size 0; expected $...-32 +main.go:1: [s390x] returnbyte: invalid MOVD of ret+8(FP); byte is 1-byte value +main.go:1: [s390x] returnbyte: invalid MOVH of ret+8(FP); byte is 1-byte value +main.go:1: [s390x] returnbyte: invalid MOVW of ret+8(FP); byte is 1-byte value +main.go:1: [s390x] returnbyte: invalid offset ret+7(FP); expected ret+8(FP) +main.go:1: [s390x] returnint: invalid MOVB of ret+0(FP); int is 8-byte value +main.go:1: [s390x] returnint: invalid MOVH of ret+0(FP); int is 8-byte value +main.go:1: [s390x] returnint: invalid MOVW of ret+0(FP); int is 8-byte value +main.go:1: [s390x] returnint: invalid offset ret+1(FP); expected ret+0(FP) +main.go:1: [s390x] returnint: unknown variable r; offset 0 is ret+0(FP) +main.go:1: [s390x] returnintmissing: RET without writing to 8-byte ret+0(FP) +main.go:1: [s390x] returnnamed: invalid MOVW of r1+8(FP); int is 8-byte value diff --git a/sonar-go-plugin/src/test/resources/externalreport/checkstyle-different-severity.xml b/sonar-go-plugin/src/test/resources/externalreport/checkstyle-different-severity.xml new file mode 100644 index 00000000..8571e5b5 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/checkstyle-different-severity.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/sonar-go-plugin/src/test/resources/externalreport/golandci-lint-report.xml b/sonar-go-plugin/src/test/resources/externalreport/golandci-lint-report.xml new file mode 100644 index 00000000..1ea18144 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/golandci-lint-report.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sonar-go-plugin/src/test/resources/externalreport/golint-report-with-error.txt b/sonar-go-plugin/src/test/resources/externalreport/golint-report-with-error.txt new file mode 100644 index 00000000..5700c631 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/golint-report-with-error.txt @@ -0,0 +1,2 @@ +xyz +main.go:1:: package comment should be of the form "Package samples ..." diff --git a/sonar-go-plugin/src/test/resources/externalreport/golint-report-with-wrong-file.txt b/sonar-go-plugin/src/test/resources/externalreport/golint-report-with-wrong-file.txt new file mode 100644 index 00000000..8f4932eb --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/golint-report-with-wrong-file.txt @@ -0,0 +1 @@ +foo.go:1:: package comment should be of the form "Package samples ..." diff --git a/sonar-go-plugin/src/test/resources/externalreport/golint-report.txt b/sonar-go-plugin/src/test/resources/externalreport/golint-report.txt new file mode 100644 index 00000000..fdb4fd7e --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/golint-report.txt @@ -0,0 +1,2 @@ +main.go:1:: package comment should be of the form "Package samples ..." +main.go:2:6: exported type User should have comment or be unexported diff --git a/sonar-go-plugin/src/test/resources/externalreport/golint-with-unknown-message.txt b/sonar-go-plugin/src/test/resources/externalreport/golint-with-unknown-message.txt new file mode 100644 index 00000000..74b86a84 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/golint-with-unknown-message.txt @@ -0,0 +1 @@ +main.go:1:: this is an unknown message \ No newline at end of file diff --git a/sonar-go-plugin/src/test/resources/externalreport/gometalinter-report-with-error.txt b/sonar-go-plugin/src/test/resources/externalreport/gometalinter-report-with-error.txt new file mode 100644 index 00000000..72e89625 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/gometalinter-report-with-error.txt @@ -0,0 +1,2 @@ +invalid-invalid-invalid +main.go:1:6:warning: exported type User should have comment or be unexported (golint) diff --git a/sonar-go-plugin/src/test/resources/externalreport/gometalinter-report.txt b/sonar-go-plugin/src/test/resources/externalreport/gometalinter-report.txt new file mode 100644 index 00000000..3862cb59 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/gometalinter-report.txt @@ -0,0 +1,3 @@ +main.go:1::error: self-assignment of name to name (vet) +main.go:2:5:warning: other (declaration) of ascii_allowed (interfacer) +main.go:3:6:warning: exported type User should have comment or be unexported (golint) diff --git a/sonar-go-plugin/src/test/resources/externalreport/govet-report-with-error.txt b/sonar-go-plugin/src/test/resources/externalreport/govet-report-with-error.txt new file mode 100644 index 00000000..b6a35fa4 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/govet-report-with-error.txt @@ -0,0 +1,3 @@ +main.go:1: comparison of function Foo == nil is always false +abcdefghijkl +exit status 1 diff --git a/sonar-go-plugin/src/test/resources/externalreport/govet-report.txt b/sonar-go-plugin/src/test/resources/externalreport/govet-report.txt new file mode 100644 index 00000000..6d6999b9 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/govet-report.txt @@ -0,0 +1,4 @@ +main.go:1: comparison of function Foo == nil is always false +main.go:2: Printf format %s has arg &str of wrong type *string +main.go:2: unreachable code +exit status 1 diff --git a/sonar-go-plugin/src/test/resources/externalreport/govet-with-unknown-message.txt b/sonar-go-plugin/src/test/resources/externalreport/govet-with-unknown-message.txt new file mode 100644 index 00000000..37e72cdb --- /dev/null +++ b/sonar-go-plugin/src/test/resources/externalreport/govet-with-unknown-message.txt @@ -0,0 +1 @@ +main.go:1: this is an unknown message \ No newline at end of file diff --git a/sonar-go-plugin/src/test/resources/myProject/foo_test.go b/sonar-go-plugin/src/test/resources/myProject/foo_test.go new file mode 100644 index 00000000..e69de29b diff --git a/sonar-go-plugin/src/test/resources/myProject/packageFoo/foo_test.go b/sonar-go-plugin/src/test/resources/myProject/packageFoo/foo_test.go new file mode 100644 index 00000000..e69de29b diff --git a/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/bar_test.go b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/bar_test.go new file mode 100644 index 00000000..e69de29b diff --git a/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/foo_test.go b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/foo_test.go new file mode 100644 index 00000000..e69de29b diff --git a/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/module_report.json b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/module_report.json new file mode 100644 index 00000000..347cd808 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/module_report.json @@ -0,0 +1,19 @@ +{"Time":"2019-05-28T16:23:38.1469798+02:00","Action":"run","Package":"my/module","Test":"TestMultiply"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply","Output":"=== RUN TestMultiply\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"run","Package":"my/module","Test":"TestMultiply/negative_multiplication"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply/negative_multiplication","Output":"=== RUN TestMultiply/negative_multiplication\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"run","Package":"my/module","Test":"TestMultiply/x_less_than_y"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply/x_less_than_y","Output":"=== RUN TestMultiply/x_less_than_y\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"run","Package":"my/module","Test":"TestMultiply/y_less_than_x"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply/y_less_than_x","Output":"=== RUN TestMultiply/y_less_than_x\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply","Output":"--- PASS: TestMultiply (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply/negative_multiplication","Output":" --- PASS: TestMultiply/negative_multiplication (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"my/module","Test":"TestMultiply/negative_multiplication","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply/x_less_than_y","Output":" --- PASS: TestMultiply/x_less_than_y (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"my/module","Test":"TestMultiply/x_less_than_y","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Test":"TestMultiply/y_less_than_x","Output":" --- PASS: TestMultiply/y_less_than_x (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"my/module","Test":"TestMultiply/y_less_than_x","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"my/module","Test":"TestMultiply","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"my/module","Output":"PASS\n"} +{"Time":"2019-05-28T16:23:38.1590056+02:00","Action":"output","Package":"my/module","Output":"ok \tgithub.com/myOrg/myProject\t0.321s\n"} +{"Time":"2019-05-28T16:23:38.1590056+02:00","Action":"pass","Package":"my/module","Elapsed":0.322} diff --git a/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/mul_test.go b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/mul_test.go new file mode 100644 index 00000000..a512f0b1 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/mul_test.go @@ -0,0 +1,28 @@ +package something + +import ( + "github.com/stretchr/testify/assert" + "testing" +) + +func multiply(x int, y int) int { + return x * y +} + +func TestMultiply(t *testing.T) { + tests := map[string]struct { + x int + y int + expected int + }{ + "x less than y": {x: 1, y: 2, expected: 2}, + "y less than x": {x: 2, y: 1, expected: 2}, + "negative multiplication": {x: 2, y: -1, expected: -2}, + } + for name, test := range tests { + t.Run(name, func(t *testing.T) { + result := multiply(test.x, test.y) + assert.Equal(t, test.expected, result) + }) + } +} diff --git a/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/report.out b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/report.out new file mode 100644 index 00000000..d85041a7 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/report.out @@ -0,0 +1,29 @@ +{"Time":"","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestFoo1"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestFoo1","Output":"=== RUN Test1\n"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestFoo1","Output":"--- PASS: Test1 (0.001s)\n"} +{"Time":"","Action":"pass","Package":"github.com/myOrg/myProject","Test":"TestFoo1","Elapsed":0.001} +invalid line +{"Time":"","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestFoo2"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestFoo2","Output":"=== RUN Test1\n"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestFoo2","Output":"--- PASS: Test1 (0.001s)\n"} +{"Time":"","Action":"fail","Package":"github.com/myOrg/myProject","Test":"TestFoo2","Elapsed":0.002} + +{"Time":"","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestBar"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestBar","Output":"=== RUN Test1\n"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestBar","Output":"--- PASS: Test1 (0.001s)\n"} +{"Time":"","Action":"skip","Package":"github.com/myOrg/myProject","Test":"TestBar","Elapsed":0.007} + +{"Time":"","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestINVALID"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestINVALID","Output":"=== RUN Test1\n"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestINVALID","Output":"--- PASS: Test1 (0.001s)\n"} +{"Time":"","Action":"skip","Package":"github.com/myOrg/myProject","Test":"TestINVALID","Elapsed":0.007} + +{"Time":"","Action":"run","Package":"github.com/myOrg/INVALID","Test":"TestBar"} +{"Time":"","Action":"output","Package":"github.com/myOrg/INVALID","Test":"TestBar","Output":"=== RUN Test1\n"} +{"Time":"","Action":"output","Package":"github.com/myOrg/INVALID","Test":"TestBar","Output":"--- PASS: Test1 (0.001s)\n"} +{"Time":"","Action":"skip","Package":"github.com/myOrg/INVALID","Test":"TestBar","Elapsed":0.007} + +{ invalid json } +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Output":"PASS\n"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Output":"ok \tgithub.com/myOrg/myProject\t0.010s\n"} +{"Time":"","Action":"pass","Package":"github.com/myOrg/myProject","Elapsed":0.010} diff --git a/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/report1.out b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/report1.out new file mode 100644 index 00000000..2a86c6b8 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/report1.out @@ -0,0 +1,4 @@ +{"Time":"","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestFoo1"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestFoo1","Output":"=== RUN Test1\n"} +{"Time":"","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestFoo1","Output":"--- PASS: Test1 (0.001s)\n"} +{"Time":"","Action":"pass","Package":"github.com/myOrg/myProject","Test":"TestFoo1","Elapsed":0.001} diff --git a/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/subtest_report.json b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/subtest_report.json new file mode 100644 index 00000000..be3abb12 --- /dev/null +++ b/sonar-go-plugin/src/test/resources/testReportGoPath/src/github.com/myOrg/myProject/subtest_report.json @@ -0,0 +1,19 @@ +{"Time":"2019-05-28T16:23:38.1469798+02:00","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestMultiply"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply","Output":"=== RUN TestMultiply\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestMultiply/negative_multiplication"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply/negative_multiplication","Output":"=== RUN TestMultiply/negative_multiplication\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestMultiply/x_less_than_y"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply/x_less_than_y","Output":"=== RUN TestMultiply/x_less_than_y\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"run","Package":"github.com/myOrg/myProject","Test":"TestMultiply/y_less_than_x"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply/y_less_than_x","Output":"=== RUN TestMultiply/y_less_than_x\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply","Output":"--- PASS: TestMultiply (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply/negative_multiplication","Output":" --- PASS: TestMultiply/negative_multiplication (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"github.com/myOrg/myProject","Test":"TestMultiply/negative_multiplication","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply/x_less_than_y","Output":" --- PASS: TestMultiply/x_less_than_y (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"github.com/myOrg/myProject","Test":"TestMultiply/x_less_than_y","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Test":"TestMultiply/y_less_than_x","Output":" --- PASS: TestMultiply/y_less_than_x (0.00s)\n"} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"github.com/myOrg/myProject","Test":"TestMultiply/y_less_than_x","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"pass","Package":"github.com/myOrg/myProject","Test":"TestMultiply","Elapsed":0} +{"Time":"2019-05-28T16:23:38.1479765+02:00","Action":"output","Package":"github.com/myOrg/myProject","Output":"PASS\n"} +{"Time":"2019-05-28T16:23:38.1590056+02:00","Action":"output","Package":"github.com/myOrg/myProject","Output":"ok \tgithub.com/myOrg/myProject\t0.321s\n"} +{"Time":"2019-05-28T16:23:38.1590056+02:00","Action":"pass","Package":"github.com/myOrg/myProject","Elapsed":0.322} diff --git a/sonar-go-to-slang/.gitignore b/sonar-go-to-slang/.gitignore new file mode 100644 index 00000000..96a6f41b --- /dev/null +++ b/sonar-go-to-slang/.gitignore @@ -0,0 +1,7 @@ +.gogradle/ +# Generated file +goparser_generated.go +# Binary which can be build +slang-generator-go +# libraries used for tests +vendor/ diff --git a/sonar-go-to-slang/Makefile b/sonar-go-to-slang/Makefile new file mode 100644 index 00000000..5cf7a8c9 --- /dev/null +++ b/sonar-go-to-slang/Makefile @@ -0,0 +1,23 @@ +GO_VERSION=1.21.1 +GO_BINARY=go$(GO_VERSION) + +all: goparser_generated.go + +install_go: + go install golang.org/dl/go"${GO_VERSION}@latest" + $(GO_BINARY) download + + +goparser_generated.go: generate_source.go install_go + $(GO_BINARY) run generate_source.go + GOOS=darwin GOARCH=amd64 $(GO_BINARY) build -o build/sonar-go-to-slang-darwin-amd64 + GOOS=linux GOARCH=amd64 $(GO_BINARY) build -o build/sonar-go-to-slang-linux-amd64 + GOOS=windows GOARCH=amd64 $(GO_BINARY) build -o build/sonar-go-to-slang-windows-amd64.exe + +test-report.out: install_go + $(GO_BINARY) test -json > test-report.out + + +clean: + rm -f goparser_generated.go + rm -f build/sonar-go-to-slang-* diff --git a/sonar-go-to-slang/README.md b/sonar-go-to-slang/README.md new file mode 100644 index 00000000..5fe3e447 --- /dev/null +++ b/sonar-go-to-slang/README.md @@ -0,0 +1,55 @@ +# sonar-go-to-slang + +Generate slang serialized AST in JSON from a go source file. + +## Building + +To generate `goparser_generated.go` file in current directory, run: + + go generate + +To create `sonar-go-to-slang` executable in current directory, run: + + go build + +To create `sonar-go-to-slang` executable in `$GOPATH/bin`, run: + + go install + +### Building on Windows + +When trying to build `sonar-go-to-slang` on Windows, the build may fail with the following error: + + > Create symbolic link at [...]\slang\sonar-go-to-slang\.gogradle\project_gopath\src\github.com\SonarSource\slang\sonar-go-to-slang failed + +Creating the symbolic link by hand solves this problem: + +* (Eventually enable [developer mode in Windows](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development)) + +* Run (in `sonar-go-to-slang` folder): + + + mklink /D ".gogradle\project_gopath\src\github.com\SonarSource\slang\sonar-go-to-slang" "Absolute\Path\To\slang\sonar-go-to-slang" + + +## Running + +If you have `$GOPATH/bin` on your `PATH`, it's easy to run with `slang-generator-go`. + +Run with `-h` or `-help` or `--help` to get usage help. + +Print the SLANG Json tree for some `source.go`: + + sonar-go-to-slang source.go + +Dump the native raw AST for some `source.go`: + + sonar-go-to-slang -d source.go + +## Testing + +To perform the tests, run: + + go test + +To update test results, use the method `fix_all_go_files_test_automatically` in `goparser_test.go` diff --git a/sonar-go-to-slang/build.gradle b/sonar-go-to-slang/build.gradle new file mode 100644 index 00000000..34857bcb --- /dev/null +++ b/sonar-go-to-slang/build.gradle @@ -0,0 +1,31 @@ +sonarqube { + properties { + property 'sonar.sources', '.' + property 'sonar.inclusions', '**/*.go' + property 'sonar.exclusions', '**/render.go,**/generate_source.go,**/*_generated.go,**/build/**,**/vendor/**,**/.gogradle/**' + property 'sonar.tests', '.' + property 'sonar.test.inclusions', '**/*_test.go' + property 'sonar.test.exclusions', '**/build/**,**/vendor/**,**/.gogradle/**' + property 'sonar.go.tests.reportPaths', "${project.projectDir}/.gogradle/reports/test-report.out" + property 'sonar.go.coverage.reportPaths', "${project.projectDir}/.gogradle/reports/coverage/profiles/github.com%2FSonarSource%2Fslang%2Fsonar-go-to-slang.out" + } +} + +task generateParserAndBuild(type: Exec) { + commandLine "./make.sh" + args "build" +} + +task generateTestReport(type: Exec) { + commandLine "./make.sh" + args "generate-test-report" +} + +task cleanTask(type: Exec) { + commandLine "./make.sh" + args "clean" +} + +clean.dependsOn cleanTask +generateTestReport.dependsOn generateParserAndBuild +build.dependsOn generateTestReport diff --git a/sonar-go-to-slang/generate_source.go b/sonar-go-to-slang/generate_source.go new file mode 100644 index 00000000..9a89371f --- /dev/null +++ b/sonar-go-to-slang/generate_source.go @@ -0,0 +1,477 @@ +// SonarQube Go Plugin +// Copyright (C) 2018-2024 SonarSource SA +// mailto:info AT sonarsource DOT com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +// The following directive is necessary to make the package coherent: +//go:build ignore +// +build ignore + +// This program generates 'goparser_generated.go'. It can be invoked by running "go generate" +package main + +import ( + "bytes" + "go/ast" + "io/ioutil" + "reflect" +) + +func main() { + var out bytes.Buffer + + out.WriteString(`// Code generated by 'generate_source.go' using 'go run generate_source.go'; DO NOT EDIT. +package main + +import ( + "go/ast" + "go/token" + "strconv" +) +`) + + context := AstContext{ + Out: &out, + ArrayFieldCreatingNode: map[string]bool{ + // By default when a field is an array, an intermediate node is not created to store the array elements. + // Array elements are appended directly to the parent. And it's not possible to define "kinds" like + // "CaseClause#List" because there's no matching node, but only "CaseClause#List[i]". + // But adding an entry below, change the default behavior and create an intermediate node, and this node + // support "kinds" defined in "KindsPerName". + "File#Decls": true, + "GenDecl#Specs": true, + "Field#Names": true, + "FieldResult#Names": true, + "FieldParam#Names": true, + "ValueSpec#Names": true, + "ValueSpec#Values": true, + "AssignStmt#Lhs": true, + "AssignStmt#Rhs": true, + "CaseClause#Body": true, + "CommClause#Body": true, + "CallExpr#Args": true, + "CompositeLit#Elts": true, + }, + InsertBeforeField: map[string]string{ + // Additional code can be placed before the mapping of the referenced field + "EmptyStmt#Semicolon": "if astNode.Implicit {\n\t\treturn nil\n\t}", + "FuncDecl#Recv": "children = t.appendNode(children, " + + "t.createTokenFromPosAstToken(astNode.Type.Func, token.FUNC, \"Type.Func\"))", + }, + OverrideField: map[string]string{ + // The mapping of each field can be replaced by some custom code. Put function definitions in 'goparser.go' + "File#Package": "children = t.appendNode(children, t.mapPackageDecl(astNode))", + "File#Name": "", + // unknown "case" or "default" + "CaseClause#Case": "children = t.handleSwitchCase(astNode.Case, len(astNode.List) == 0, children)", + // FIXME this is wrong as it creates DEFAULT_CASE in the body of SELECT + "CommClause#Case": "children = t.handleSwitchCase(astNode.Case, astNode.Comm == nil, children)", + "IfStmt#Init": "", + "IfStmt#Cond": "children = t.appendNode(children, " + + "t.createAdditionalInitAndCond(astNode.Init, astNode.Cond))", + "File#Imports": "", + "File#Unresolved": "", + "ChanType#Begin": "", + "ChanType#Arrow": "", + "ChanType#Dir": "", + "ImportSpec#EndPos": "", + "FuncTypeDecl#Func": "", + }, + ForceLeafNode: map[string]bool{ + // Fields from the given struct types are ignored, this produce terminal node with token + "Ident": true, + "BadStmt": true, + "BadDecl": true, + "BadExpr": true, + }, + FieldToIgnore: map[string]bool{ + "File#FileStart": true, + "File#FileEnd" : true, + "File#GoVersion" : true, + "RangeStmt#Range" : true, + }, + TypeToIgnore: map[string]bool{ + // All fields of ast.* structs with the following types are ignored + "*ast.CommentGroup": true, + "*ast.Object": true, + "*ast.Scope": true, + "bool": true, + "[]*ast.CommentGroup": true, + "map[string]*ast.File": true, + "map[string]*ast.Object": true, + }, + TokenFieldWithPos: map[string]bool{ + // There's a common pattern in the Go ast where 2 fields define one terminal token. + // The first field is a "token.Pos" and the second a "token.Token" with the same name without "Pos" suffix. + // Reference below the "token.Pos" field, and the "token.Token" field will be associated to produce one + // token. + "GenDecl#TokPos": true, + "AssignStmt#TokPos": true, + "BranchStmt#TokPos": true, + "IncDecStmt#TokPos": true, + "RangeStmt#TokPos": true, + "BinaryExpr#OpPos": true, + "UnaryExpr#OpPos": true, + }, + StructVariations: map[string][]string{ + // By default one mapping function is generated for each ast.* struct, e.g. "mapFile" for "ast.File" + // But if the caller of the mapping function needs a specific behavior (like specific: uast kinds, + // fields override, token value, ...) then several mapping functions can be generated using the + // given suffixes. For example providing the suffix "Result" for the type "Field" will generate + // the mapping function "mapFieldResult". And an alias of "Field" called "FieldResult" can be used + // to customize this mapping function. For example a field can be referenced by "FieldResult#Names" + + // variations to distinguish delimiters '('/'{' and kinds PARAMETER/RESULT/... + "FieldList": []string{"Params", "Results", "Brace", "Receiver", "TypeParams"}, + + // variations for kinds PARAMETER/RESULT + "Field": []string{"", "Result", "Param"}, + + // "Decl" variation ignore "func" keyword and is called by FuncDecl, because FuncDecl already map the + // field FuncDecl.Type.Func. + // The default variation called from FuncLit or Expr has no specificity. + "FuncType": []string{"", "Decl"}, + }, + FieldVariationMap: map[string]string{ + // Add a suffix to the mapping function of the given field. (suffixes are defined in StructVariations) + // For example, the field "StructType#Fields" has a type "ast.FieldList", so by default the mapping + // function is "mapFieldList". But by defining a suffix "Brace", then the mapping function will be + // "mapFieldListBrace". + "StructType#Fields": "Brace", + "InterfaceType#Methods": "Brace", + "FuncType#Params": "Params", + "FuncType#Results": "Results", + "FuncType#TypeParams": "TypeParams", + "FuncTypeDecl#Params": "Params", + "FuncTypeDecl#Results": "Results", + "FuncTypeDecl#TypeParams": "TypeParams", + "FuncDecl#Type": "Decl", + "FuncDecl#Recv": "Receiver", + "FieldListParams#List[i]": "Param", + "FieldListResults#List[i]": "Result", + "Field#Tag": "Tag", + "FieldResult#Tag": "Tag", + "FieldParam#Tag": "Tag", + "TypeSpec#TypeParams": "TypeParams", + }, + MatchingTokenPos: map[string]string{ + // Some ast.* struct fields with type "token.Pos" has no "token.Token" fields to specify their string + // value. The below list do the mapping. A field can be referenced just by "" (like "Lbrace") + // and will apply to all struct containing such field. Or by "#" like "IfStmt#If". + // Or by "#" like "FieldListParams#Opening". + "Lbrace": "token.LBRACE", + "Rbrace": "token.RBRACE", + "Lbrack": "token.LBRACK", + "Rbrack": "token.RBRACK", + "Lparen": "token.LPAREN", + "Rparen": "token.RPAREN", + "Colon": "token.COLON", + "Semicolon": "token.SEMICOLON", + "Star": "token.MUL", + "TypeSpec#Assign": "token.ASSIGN", + "FieldListParams#Opening": "token.LPAREN", + "FieldListParams#Closing": "token.RPAREN", + "FieldListResults#Opening": "token.LPAREN", + "FieldListResults#Closing": "token.RPAREN", + "FieldListBrace#Opening": "token.LBRACE", + "FieldListBrace#Closing": "token.RBRACE", + "FieldListReceiver#Opening": "token.LPAREN", + "FieldListReceiver#Closing": "token.RPAREN", + "FieldListTypeParams#Opening": "token.LBRACK", + "FieldListTypeParams#Closing": "token.RBRACK", + "GoStmt#Go": "token.GO", + "IfStmt#If": "token.IF", + "SendStmt#Arrow": "token.ARROW", + "Ellipsis": "token.ELLIPSIS", + "ForStmt#For": "token.FOR", + "RangeStmt#For": "token.FOR", + "MapType#Map": "token.MAP", + "FuncType#Func": "token.FUNC", + "DeferStmt#Defer": "token.DEFER", + "ReturnStmt#Return": "token.RETURN", + "SelectStmt#Select": "token.SELECT", + "SwitchStmt#Switch": "token.SWITCH", + "TypeSwitchStmt#Switch": "token.SWITCH", + "StructType#Struct": "token.STRUCT", + "InterfaceType#Interface": "token.INTERFACE", + }, + TypeQueue: []reflect.Type{ + // This queue is used to generate all the ast.* struct types. The generation is initiated by pushing + // the ast.File type. Other types will be discovered by reflection. + typeOf((*ast.File)(nil)), + }, + TypeProcessed: map[reflect.Type]bool{ + //Node already implemented inside goparser.go + typeOf((*ast.BasicLit)(nil)): true, + }, + AllAstStruct: typeOfList( + // "Go" does not provide a way to enumerate struct types that inherit from a given interface. + // But filtering a list of struct types using "struct.Implements(interface)" method is possible. + // Generated by: grep 'struct {' go/binary/1.10/go/src/go/ast/ast.go | sed -r 's/^\t*(type )*([^ ]+).*/(*ast.\2)(nil),/' | sort + (*ast.ArrayType)(nil), (*ast.AssignStmt)(nil), (*ast.BadDecl)(nil), + (*ast.BadExpr)(nil), (*ast.BadStmt)(nil), (*ast.BasicLit)(nil), (*ast.BinaryExpr)(nil), + (*ast.BlockStmt)(nil), (*ast.BranchStmt)(nil), (*ast.CallExpr)(nil), (*ast.CaseClause)(nil), + (*ast.ChanType)(nil), (*ast.CommClause)(nil), (*ast.CommentGroup)(nil), (*ast.Comment)(nil), + (*ast.CompositeLit)(nil), (*ast.DeclStmt)(nil), (*ast.DeferStmt)(nil), (*ast.Ellipsis)(nil), + (*ast.EmptyStmt)(nil), (*ast.ExprStmt)(nil), (*ast.FieldList)(nil), (*ast.Field)(nil), (*ast.File)(nil), + (*ast.ForStmt)(nil), (*ast.FuncDecl)(nil), (*ast.FuncLit)(nil), (*ast.FuncType)(nil), (*ast.GenDecl)(nil), + (*ast.GoStmt)(nil), (*ast.Ident)(nil), (*ast.IfStmt)(nil), (*ast.ImportSpec)(nil), (*ast.IncDecStmt)(nil), + (*ast.IndexExpr)(nil), (*ast.IndexListExpr)(nil), (*ast.InterfaceType)(nil), (*ast.KeyValueExpr)(nil), (*ast.LabeledStmt)(nil), + (*ast.MapType)(nil), (*ast.Package)(nil), (*ast.ParenExpr)(nil), (*ast.RangeStmt)(nil), + (*ast.ReturnStmt)(nil), (*ast.SelectorExpr)(nil), (*ast.SelectStmt)(nil), (*ast.SendStmt)(nil), + (*ast.SliceExpr)(nil), (*ast.StarExpr)(nil), (*ast.StructType)(nil), (*ast.SwitchStmt)(nil), + (*ast.TypeAssertExpr)(nil), (*ast.TypeSpec)(nil), (*ast.TypeSwitchStmt)(nil), (*ast.UnaryExpr)(nil), + (*ast.ValueSpec)(nil), + ), + } + context.execute() + + err := ioutil.WriteFile("goparser_generated.go", out.Bytes(), 0644) + if err != nil { + panic(err) + } +} + +func typeOf(pointer interface{}) reflect.Type { + return reflect.TypeOf(pointer).Elem() +} + +func typeOfList(pointerList ...interface{}) []reflect.Type { + types := make([]reflect.Type, len(pointerList)) + for i, structPointer := range pointerList { + types[i] = reflect.TypeOf(structPointer).Elem() + } + return types +} + +type AstContext struct { + Out *bytes.Buffer + TypeToIgnore map[string]bool + ForceLeafNode map[string]bool + FieldToIgnore map[string]bool + TokenFieldWithPos map[string]bool + MatchingTokenPos map[string]string + ArrayFieldCreatingNode map[string]bool + KindsPerTypeException map[reflect.Type]reflect.Type + KindsPerName map[string]string + InsertBeforeField map[string]string + OverrideField map[string]string + StructVariations map[string][]string + FieldVariationMap map[string]string + TypeQueue []reflect.Type + TypeProcessed map[reflect.Type]bool + AllAstStruct []reflect.Type +} + +func (t *AstContext) execute() { + for len(t.TypeQueue) > 0 { + nextType := t.TypeQueue[0] + t.TypeQueue = t.TypeQueue[1:] + t.TypeProcessed[nextType] = true + t.visitType(nextType) + } +} + +func (t *AstContext) pushType(structType reflect.Type) { + if !t.TypeProcessed[structType] && !t.TypeToIgnore[structType.String()] { + t.TypeProcessed[structType] = true + t.TypeQueue = append(t.TypeQueue, structType) + } +} + +func (t *AstContext) writeLn(text string) { + t.Out.WriteString(text) + t.Out.WriteString("\n") +} + +func (t *AstContext) visitType(nextType reflect.Type) { + switch nextType.Kind() { + case reflect.Interface: + t.visitInterfaceType(nextType) + case reflect.Struct: + t.visitStructType(nextType) + default: + panic("Unsupported Kind " + nextType.Kind().String() + " for type " + nextType.String()) + } +} + +func (t *AstContext) visitInterfaceType(interfaceType reflect.Type) { + if interfaceType.Kind() != reflect.Interface { + panic("Expect a Interface") + } + t.writeLn("") + methodName := "map" + interfaceType.Name() + arguments := "astNode " + interfaceType.String() + ", nativeNode string" + t.writeLn("func (t *SlangMapper) " + methodName + "(" + arguments + ") *Node {") + t.writeLn("\tswitch node := astNode.(type) {") + for _, astStruct := range t.getStructTypesThatImplement(interfaceType) { + if !t.TypeToIgnore[astStruct.String()] { + t.writeLn("\tcase *" + astStruct.String() + ":") + t.writeLn("\t\treturn t.map" + astStruct.Name() + "(node, nativeNode)") + t.pushType(astStruct) + } else { + t.writeLn("\t// ignore " + astStruct.String() + " intentionally") + } + } + t.writeLn("\tdefault:") + t.writeLn("\t\treturn nil") + t.writeLn("\t}") + t.writeLn("}") +} + +func (t *AstContext) visitStructType(structType reflect.Type) { + if structType.Kind() != reflect.Struct { + panic("Expect a Struct") + } + variations := t.StructVariations[structType.Name()] + if len(variations) == 0 { + variations = []string{""} + } + for _, variation := range variations { + t.writeLn("") + methodName := "map" + structType.Name() + variation + arguments := "astNode *" + structType.String() + ", fieldName string" + t.writeLn("func (t *SlangMapper) " + methodName + "(" + arguments + ") *Node {") + t.writeLn("\tif astNode == nil {") + t.writeLn("\t\treturn nil") + t.writeLn("\t}") + t.writeLn("\tnodeImpl := t." + methodName + "Impl(astNode, fieldName)") + t.writeLn("\tif nodeImpl != nil {") + t.writeLn("\t\treturn nodeImpl") + t.writeLn("\t}") + t.writeLn("\tvar children []*Node") + if !t.ForceLeafNode[structType.Name()] { + for i := 0; i < structType.NumField(); i++ { + field := structType.Field(i) + fullName := structType.Name() + variation + "#" + field.Name + t.visitField(fullName, structType, field) + } + } + arguments = "astNode, children, fieldName + \"(" + structType.Name() + ")\"" + t.writeLn("\treturn t.createNativeNode(" + arguments + ")") + t.writeLn("}") + } +} + +func (t *AstContext) visitField(fullName string, structType reflect.Type, field reflect.StructField) { + codeBefore := t.InsertBeforeField[fullName] + if len(codeBefore) > 0 { + t.writeLn("\t" + codeBefore) + } + + overrideCode, isOverridden := t.OverrideField[fullName] + if isOverridden && len(overrideCode) > 0 { + t.writeLn("\t" + overrideCode) + } + + fieldType := field.Type + + if !t.TypeToIgnore[fieldType.String()] && !t.FieldToIgnore[fullName] { + if !isOverridden && !t.TypeToIgnore[fieldType.String()] && !t.FieldToIgnore[fullName] { + switch fieldType.Kind() { + case reflect.Ptr, reflect.Struct, reflect.Interface: + t.visitStructField(fullName, field.Name, fieldType) + case reflect.Slice: + t.visitSliceField(fullName, field.Name, fieldType) + case reflect.Int: + t.visitIntField(fullName, field, fieldType) + default: + panic("Unsupported Kind " + fieldType.Kind().String() + " for type " + fieldType.String()) + } + } + } +} + +func (t *AstContext) visitStructField(fullName, name string, fieldType reflect.Type) { + mappedField := t.mapField(fullName, fieldType, "astNode."+name, name) + t.writeLn("\tchildren = t.appendNode(children, " + mappedField + ")") +} + +func (t *AstContext) visitSliceField(fullName, name string, sliceType reflect.Type) { + elemType := sliceType.Elem() + parentListName := "children" + isParentList := t.ArrayFieldCreatingNode[fullName] + if isParentList { + parentListName = "nodeList" + name + t.writeLn("\tvar " + parentListName + " []*Node") + } + t.writeLn("\tfor i := 0; i < len(astNode." + name + "); i++ {") + fieldMapping := t.mapField(fullName+"[i]", elemType, "astNode."+name+"[i]", "[\" + strconv.Itoa(i) + \"]") + t.writeLn("\t\t" + parentListName + " = t.appendNode(" + parentListName + ", " + fieldMapping + ")") + t.writeLn("\t}") + if isParentList { + var typeName string + if elemType.Kind() == reflect.Ptr { + typeName = "*" + elemType.Elem().Name() + } else { + typeName = elemType.Name() + } + arguments := "children, " + parentListName + ", \"" + name + "([]" + typeName + ")\"" + t.writeLn("\tchildren = t.appendNodeList(" + arguments + ")") + } +} + +func (t *AstContext) visitIntField(fullName string, field reflect.StructField, fieldType reflect.Type) { + if fieldType.String() == "token.Pos" && t.TokenFieldWithPos[fullName] { + // ignore, will be added by the "token.Token" field + return + } + var tokenPos string + var tokenValue string + if fieldType.String() == "token.Token" && t.TokenFieldWithPos[fullName+"Pos"] { + tokenPos = "astNode." + field.Name + "Pos" + tokenValue = "astNode." + field.Name + } else if fieldType.String() == "token.Pos" { + tokenPos = "astNode." + field.Name + tokenValue = t.MatchingTokenPos[fullName] + if len(tokenValue) == 0 { + tokenValue = t.MatchingTokenPos[field.Name] + } + } + if len(tokenValue) > 0 { + arguments := tokenPos + ", " + tokenValue + ", \"" + field.Name + "\"" + mappedField := "t.createTokenFromPosAstToken(" + arguments + ")" + t.writeLn("\tchildren = t.appendNode(children, " + mappedField + ")") + } else { + panic("Unsupported Int Kind " + fullName + " " + fieldType.String()) + } +} + +func (t *AstContext) mapField(fullName string, fieldType reflect.Type, name, fieldName string) string { + addressPrefix := "&" + if fieldType.Kind() == reflect.Ptr { + fieldType = fieldType.Elem() + addressPrefix = "" + } + if fieldType.Kind() == reflect.Interface { + addressPrefix = "" + } else if fieldType.Kind() != reflect.Struct { + panic("Unsupported Kind " + fieldType.Kind().String() + " for " + name + " " + fieldType.String()) + } + t.pushType(fieldType) + methodMane := "t.map" + fieldType.Name() + t.FieldVariationMap[fullName] + return methodMane + "(" + addressPrefix + name + ", \"" + fieldName + "\")" +} + +func (t *AstContext) getStructTypesThatImplement(interfaceType reflect.Type) []reflect.Type { + list := []reflect.Type{} + for _, astStruct := range t.AllAstStruct { + if reflect.PtrTo(astStruct).Implements(interfaceType) { + list = append(list, astStruct) + } + } + return list +} diff --git a/sonar-go-to-slang/go.mod b/sonar-go-to-slang/go.mod new file mode 100644 index 00000000..81809ed5 --- /dev/null +++ b/sonar-go-to-slang/go.mod @@ -0,0 +1,3 @@ +module github.com/SonarSource/slang/sonar-go-to-slang + +go 1.21 diff --git a/sonar-go-to-slang/goparser.go b/sonar-go-to-slang/goparser.go new file mode 100644 index 00000000..cc576362 --- /dev/null +++ b/sonar-go-to-slang/goparser.go @@ -0,0 +1,578 @@ +// SonarQube Go Plugin +// Copyright (C) 2018-2024 SonarSource SA +// mailto:info AT sonarsource DOT com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +package main + +//go:generate go run generate_source.go + +import ( + "bytes" + "errors" + "fmt" + "go/ast" + "go/parser" + "go/token" + "io/ioutil" + "os" + "strings" + "unicode/utf8" +) + +type Token struct { + Value string `json:"text"` + TextRange *TextRange `json:"textRange"` + TokenType string `json:"type"` +} + +type Node struct { + Token *Token `json:"-"` + Children []*Node `json:"-"` + // internal fields + offset int // position of first character belonging to the node + endOffset int // position of first character immediately after the node + //Slang fields + SlangType string `json:"@type"` + TextRange *TextRange `json:"metaData"` + SlangField map[string]interface{} `json:"slangF"` +} + +type TextRange struct { + StartLine int + StartColumn int + EndLine int + EndColumn int +} + +const keywordKind = "KEYWORD" +const nativeSlangType = "Native" +const nativeKind = "nativeKind" +const childrenField = "children" +const basicLiteral = "(BasicLit)" +const other = "OTHER" + +var isSlangType = map[string]bool{ + other: true, keywordKind: true, "STRING_LITERAL": true} + +func toSlangTree(fileSet *token.FileSet, astFile *ast.File, fileContent string) (*Node, []*Node, []*Token) { + return NewSlangMapper(fileSet, astFile, fileContent).toSlang() +} + +func readAstFile(filename string) (fileSet *token.FileSet, astFile *ast.File, fileContent string, err error) { + var bytes []byte + if filename == "-" { + bytes, err = ioutil.ReadAll(os.Stdin) + } else { + bytes, err = ioutil.ReadFile(filename) + } + if err != nil { + return + } + fileContent = string(bytes) + fileSet, astFile, err = readAstString(filename, fileContent) + return +} + +func readAstString(filename string, fileContent string) (fileSet *token.FileSet, astFile *ast.File, err error) { + fileSet = token.NewFileSet() + astFile, err = parser.ParseFile(fileSet, filename, fileContent, parser.ParseComments) + if err != nil { + return + } + fileSize := fileSet.File(astFile.Pos()).Size() + if len(fileContent) != fileSize { + err = errors.New(fmt.Sprintf("Unexpected file size, expect %d instead of %d for file %s", + len(fileContent), fileSize, filename)) + } + return +} + +type SlangMapper struct { + astFile *ast.File + fileContent string + hasCarriageReturn bool + file *token.File + comments []*Node + commentPos int + tokens []*Token + paranoiac bool +} + +func NewSlangMapper(fileSet *token.FileSet, astFile *ast.File, fileContent string) *SlangMapper { + t := &SlangMapper{ + astFile: astFile, + fileContent: fileContent, + hasCarriageReturn: strings.IndexByte(fileContent, '\r') != -1, + file: fileSet.File(astFile.Pos()), + tokens: nil, + paranoiac: true, + } + t.comments = t.mapAllComments() + t.commentPos = 0 + return t +} + +func (t *SlangMapper) toSlang() (*Node, []*Node, []*Token) { + compilationUnit := t.mapFile(t.astFile, "") + t.addEof(compilationUnit) + if t.paranoiac && (compilationUnit.offset < 0 || compilationUnit.endOffset > len(t.fileContent)) { + panic("Unexpected compilationUnit" + t.location(compilationUnit.offset, compilationUnit.endOffset)) + } + return compilationUnit, t.comments, t.tokens +} + +func (t *SlangMapper) addEof(compilationUnit *Node) { + offset := len(t.fileContent) + eofNode := t.createToken(offset, offset, "", "EOF") + compilationUnit.Children = t.appendNode(compilationUnit.Children, eofNode) + //Update the range of the top level tree to include everything until the end + compilationUnit.TextRange.EndLine = eofNode.TextRange.EndLine + compilationUnit.TextRange.EndColumn = eofNode.TextRange.EndColumn +} + +func (t *SlangMapper) mapAllComments() []*Node { + var list []*Node + for _, commentGroup := range t.astFile.Comments { + for _, comment := range commentGroup.List { + node := t.createExpectedToken(comment.Pos(), comment.Text, "", "COMMENT") + list = append(list, node) + } + } + return list +} + +func (t *SlangMapper) mapPackageDecl(file *ast.File) *Node { + var children []*Node + // "package" node is the very first node, header comments are appended before + packageNode := t.createExpectedToken(file.Package, token.PACKAGE.String(), "", keywordKind) + if packageNode != nil { + children = t.appendCommentOrMissingToken(children, 0, packageNode.offset) + children = append(children, packageNode) + } + children = t.appendNode(children, t.mapIdent(file.Name, "Name")) + + slangField := make(map[string]interface{}) + slangField[childrenField] = t.filterOutComments(children) + + return t.createNode(file, children, "File.Package", "PackageDeclaration", slangField) +} + +func (t *SlangMapper) mapBasicLitTag(astNode *ast.BasicLit, fieldName string) *Node { + if astNode == nil { + return nil + } + var tokenType = other + return t.createExpectedToken(astNode.Pos(), astNode.Value, fieldName+basicLiteral, tokenType) +} + +func (t *SlangMapper) mapBasicLit(astNode *ast.BasicLit, fieldName string) *Node { + if astNode == nil { + return nil + } + slangField := make(map[string]interface{}) + var slangType string + var tokenType = other + + switch astNode.Kind { + case token.STRING: + if strings.HasPrefix(astNode.Value, "`") { + // discard multi-line strings + return t.createExpectedToken(astNode.Pos(), astNode.Value, fieldName+basicLiteral, tokenType) + } + slangType = "StringLiteral" + tokenType = "STRING_LITERAL" + slangField["content"] = astNode.Value[1 : len(astNode.Value)-1] + slangField["value"] = astNode.Value + return t.createExpectedNode(astNode.Pos(), astNode.Value, fieldName+"(StringLit)", tokenType, slangType, slangField) + case token.INT: + slangType = "IntegerLiteral" + slangField["value"] = astNode.Value + return t.createExpectedNode(astNode.Pos(), astNode.Value, fieldName+"(IntLit)", tokenType, slangType, slangField) + default: + //Binary literal are expected in GO 1.13 (https://github.com/golang/go/issues/19308) + return t.createExpectedToken(astNode.Pos(), astNode.Value, fieldName+basicLiteral, tokenType) + } +} + +func (t *SlangMapper) appendNode(children []*Node, child *Node) []*Node { + if child == nil { + return children + } + // Comments are not appended before the first child. They will be appended by an + // ancestor node before a non first child (except for the "package" node, it's the + // very first node, it has his specific logic to append header comments) + if len(children) > 0 { + lastChild := children[len(children)-1] + children = t.appendCommentOrMissingToken(children, lastChild.endOffset, child.offset) + if t.paranoiac && children[len(children)-1].endOffset > child.offset { + panic("Invalid token sequence" + t.location(children[len(children)-1].endOffset, child.offset)) + } + } + return t.appendNodeCheckOrder(children, child) +} + +func (t *SlangMapper) createAdditionalInitAndCond(astInit ast.Stmt, astCond ast.Expr) *Node { + var children []*Node + children = t.appendNode(children, t.mapStmt(astInit, "Init")) + children = t.appendNode(children, t.mapExpr(astCond, "Cond")) + return t.createNativeNode(nil, children, "InitAndCond") +} + +func (t *SlangMapper) appendCommentOrMissingToken(children []*Node, offset, endOffset int) []*Node { + if len(t.comments) == 0 { + return t.appendMissingToken(children, offset, endOffset) + } + // when a child append a comment, it move the 'commentPos' forward, so the parent has to rewind + for t.commentPos > 0 && t.comments[t.commentPos-1].offset >= offset { + t.commentPos-- + } + + for t.commentPos < len(t.comments) { + commentNode := t.comments[t.commentPos] + if commentNode.offset >= offset { + if commentNode.endOffset <= endOffset { + children = t.appendMissingToken(children, offset, commentNode.offset) + children = t.appendNodeCheckOrder(children, commentNode) + offset = commentNode.endOffset + } else { + break + } + } + t.commentPos++ + } + return t.appendMissingToken(children, offset, endOffset) +} + +func (t *SlangMapper) appendNodeCheckOrder(parentList []*Node, child *Node) []*Node { + if child == nil { + return parentList + } + if len(parentList) > 0 { + lastChild := parentList[len(parentList)-1] + if t.paranoiac && lastChild.endOffset > child.offset { + panic("Invalid token sequence" + t.location(lastChild.endOffset, child.offset)) + } + } + return append(parentList, child) +} + +func (t *SlangMapper) appendNodeList(parentList []*Node, children []*Node, nativeNode string) []*Node { + // TODO provide the next Token offset, so the last separator can be part of the children + return t.appendNode(parentList, t.createNativeNode(nil, children, nativeNode)) +} + +func (t *SlangMapper) createNativeNode(astNode ast.Node, children []*Node, nativeNode string) *Node { + slangField := make(map[string]interface{}) + slangField[childrenField] = t.filterOutComments(children) + slangField[nativeKind] = nativeNode + + return t.createNode(astNode, children, nativeNode, nativeSlangType, slangField) +} + +func (t *SlangMapper) filterOutComments(children []*Node) []*Node { + //Filter the nodes that are comments + var slangChildren []*Node + for _, child := range children { + if child.Token == nil || isSlangType[child.Token.TokenType] { + slangChildren = append(slangChildren, child) + } + } + return slangChildren +} + +func (t *SlangMapper) createNode(astNode ast.Node, children []*Node, nativeNode, slangType string, slangField map[string]interface{}) *Node { + if len(children) > 0 { + return t.createNodeWithChildren(children, slangType, slangField) + } else if slangField != nil && astNode != nil { + //We create a leaf node, that is not a Native node + offset := t.file.Offset(astNode.Pos()) + endOffset := t.file.Offset(astNode.End()) + return t.createLeafNode(offset, endOffset, nativeNode, slangType, other, slangField) + } else if astNode != nil { + //We create a node that is a token, required since the original mapping to compute the range + //In meantime, this node will have slang Native type. + offset := t.file.Offset(astNode.Pos()) + endOffset := t.file.Offset(astNode.End()) + return t.createToken(offset, endOffset, nativeNode, other) + } else { + return nil + } +} + +func (t *SlangMapper) createNativeNodeWithChildren(children []*Node, nativeNode string) *Node { + slangField := make(map[string]interface{}) + slangField[childrenField] = t.filterOutComments(children) + slangField[nativeKind] = nativeNode + + return t.createNodeWithChildren(children, nativeSlangType, slangField) +} + +func (t *SlangMapper) createNodeWithChildren(children []*Node, slangType string, slangField map[string]interface{}) *Node { + if len(children) < 1 { + return nil + } + return &Node{ + Children: children, + offset: children[0].offset, + endOffset: children[len(children)-1].endOffset, + SlangType: slangType, + TextRange: &TextRange{ + StartLine: children[0].TextRange.StartLine, + StartColumn: children[0].TextRange.StartColumn, + EndLine: children[len(children)-1].TextRange.EndLine, + EndColumn: children[len(children)-1].TextRange.EndColumn, + }, + SlangField: slangField, + } +} + +var missingTokens = map[byte]string{ + ',': ",", ';': ";", '.': ".", '[': "[", ']': "]", '=': "=", ':': ":", + 't': "type", 'r': "range", 'e': "else", 'c': "chan", '<': "<-"} + +var missingTokenNativeNode = map[string]string{ + ";": "Semicolon"} + +var isMissingTokenKeyword = map[string]bool{ + "else": true, "range": true, "type": true, "chan": true} + +func (t *SlangMapper) appendMissingToken(children []*Node, offset, endOffset int) []*Node { + if offset < 0 || endOffset < offset || endOffset > len(t.fileContent) { + return nil + } + for offset < endOffset && t.fileContent[offset] <= ' ' { + offset++ + } + for endOffset > offset && t.fileContent[endOffset-1] <= ' ' { + endOffset-- + } + for offset < endOffset { + missingTokenValue := missingTokens[t.fileContent[offset]] + tokenLength := len(missingTokenValue) + var tokenType = other + if tokenLength == 0 || t.fileContent[offset:offset+tokenLength] != missingTokenValue { + if t.paranoiac { + location := t.location(offset, endOffset) + panic(fmt.Sprintf("Invalid missing token '%s'%s", t.fileContent[offset:endOffset], location)) + } + tokenLength = endOffset - offset + } else { + if isMissingTokenKeyword[missingTokenValue] { + tokenType = keywordKind + } + } + var nativeNode = missingTokenNativeNode[missingTokenValue] + missingToken := t.createToken(offset, offset+tokenLength, nativeNode, tokenType) + children = t.appendNodeCheckOrder(children, missingToken) + offset += tokenLength + for offset < endOffset && t.fileContent[offset] <= ' ' { + offset++ + } + } + return children +} + +func (t *SlangMapper) createTokenFromPosAstToken(pos token.Pos, tok token.Token, nativeNode string) *Node { + if pos == token.NoPos { + return nil + } + if !(tok.IsOperator() || tok.IsKeyword()) { + if t.paranoiac { + offset := t.file.Offset(pos) + location := t.location(offset, offset) + panic(fmt.Sprintf("Unsupported token '%s'%s", tok.String(), location)) + } + return nil + } + + return t.createExpectedToken(pos, tok.String(), nativeNode, t.getTokenKind(tok)) +} + +func (t *SlangMapper) getTokenKind(tok token.Token) string { + if tok.IsKeyword() { + return keywordKind + } else { + return other + } +} + +func (t *SlangMapper) handleSwitchCase(casePos token.Pos, isDefault bool, children []*Node) []*Node { + tok := token.CASE + if isDefault { + tok = token.DEFAULT + } + children = t.appendNode(children, t.createTokenFromPosAstToken(casePos, tok, "Case")) + return children +} + +func (t *SlangMapper) createExpectedToken(pos token.Pos, expectedValue, nativeNode, tokenType string) *Node { + if pos == token.NoPos { + return nil + } + offset := t.file.Offset(pos) + var endOffset int + endOffset, expectedValue = t.computeEndOffsetSupportingMultiLineToken(offset, expectedValue) + node := t.createToken(offset, endOffset, nativeNode, tokenType) + if node != nil && node.Token.Value != expectedValue { + if t.paranoiac { + location := t.location(offset, endOffset) + panic(fmt.Sprintf("Invalid token value '%s' instead of '%s'%s", + node.Token.Value, expectedValue, location)) + } + return nil + } + return node +} + +func (t *SlangMapper) createExpectedNode(pos token.Pos, expectedValue, nativeNode, tokenType string, slangType string, slangField map[string]interface{}) *Node { + if pos == token.NoPos { + return nil + } + offset := t.file.Offset(pos) + var endOffset int + endOffset, expectedValue = t.computeEndOffsetSupportingMultiLineToken(offset, expectedValue) + node := t.createLeafNode(offset, endOffset, nativeNode, slangType, tokenType, slangField) + if node != nil && node.Token.Value != expectedValue { + if t.paranoiac { + location := t.location(offset, endOffset) + panic(fmt.Sprintf("Invalid token value '%s' instead of '%s'%s", + node.Token.Value, expectedValue, location)) + } + return nil + } + return node +} + +func (t *SlangMapper) computeEndOffsetSupportingMultiLineToken(offset int, value string) (int, string) { + length := len(value) + endOffset := offset + length + if offset < 0 || !t.hasCarriageReturn { + return endOffset, value + } + contentLength := len(t.fileContent) + // computedEndOffset will be equal to offset + len(value) + + computedEndOffset := offset + for length > 0 && computedEndOffset < contentLength { + if t.fileContent[computedEndOffset] != '\r' { + length-- + } + computedEndOffset++ + } + if computedEndOffset != endOffset { + return computedEndOffset, t.fileContent[offset:computedEndOffset] + } + return endOffset, value +} + +func (t *SlangMapper) createToken(offset, endOffset int, nativeNode, tokenType string) *Node { + slangField := make(map[string]interface{}) + slangField[nativeKind] = nativeNode + + return t.createLeafNode(offset, endOffset, nativeNode, nativeSlangType, tokenType, slangField) +} + +func (t *SlangMapper) createLeafNode(offset, endOffset int, nativeNode, slangType, tokenType string, slangField map[string]interface{}) *Node { + if offset < 0 || endOffset < offset || endOffset > len(t.fileContent) { + location := t.location(offset, endOffset) + panic("Invalid token" + location) + } + if endOffset == offset && tokenType != "EOF" { + if t.paranoiac { + location := t.location(offset, endOffset) + panic("Invalid empty token" + location) + } + return nil + } + + startPosition := t.toPosition(offset) + endPosition := t.toPosition(endOffset) + if !startPosition.IsValid() || !endPosition.IsValid() { + if t.paranoiac { + location := t.location(offset, endOffset) + panic("Invalid token position" + location) + } + return nil + } + startLine := startPosition.Line + startLineOffset := offset - startPosition.Column + 1 + startColumn := utf8.RuneCountInString(t.fileContent[startLineOffset:offset]) + 1 + + endLine := endPosition.Line + endLineOffset := endOffset - endPosition.Column + 1 + endColumn := utf8.RuneCountInString(t.fileContent[endLineOffset:endOffset]) + 1 + + if offset > 0 && offset == len(t.fileContent) && isEndOfLine(t.fileContent[offset-1]) { + startLine++ + startColumn = 1 + } + if offset > 0 && endOffset == len(t.fileContent) && isEndOfLine(t.fileContent[endOffset-1]) { + endLine++ + endColumn = 1 + } + + slangToken := &Token{ + TextRange: &TextRange{ + StartLine: startLine, + StartColumn: startColumn, + EndLine: endLine, + EndColumn: endColumn, + }, + Value: t.fileContent[offset:endOffset], + + TokenType: tokenType, + } + + if isSlangType[tokenType] { + t.tokens = append(t.tokens, slangToken) + } + + return &Node{ + Token: slangToken, + offset: offset, + endOffset: endOffset, + SlangType: slangType, + TextRange: &TextRange{ + StartLine: startLine, + StartColumn: startColumn, + EndLine: endLine, + EndColumn: endColumn, + }, + SlangField: slangField, + } +} + +func (t *SlangMapper) toPosition(offset int) token.Position { + position := t.file.Position(t.file.Pos(offset)) + if t.paranoiac && !position.IsValid() { + panic("Invalid offset" + t.location(offset, offset)) + } + return position +} + +func (t *SlangMapper) location(offset, endOffset int) string { + var out bytes.Buffer + out.WriteString(fmt.Sprintf(" at offset %d:%d for file %s", offset, endOffset, t.file.Name())) + if 0 <= offset && offset <= t.file.Size() { + p := t.file.Position(t.file.Pos(offset)) + out.WriteString(fmt.Sprintf(":%d:%d", p.Line, p.Column)) + } + return out.String() +} + +func isEndOfLine(ch byte) bool { + return ch == '\n' || ch == '\r' +} diff --git a/sonar-go-to-slang/goparser_test.go b/sonar-go-to-slang/goparser_test.go new file mode 100644 index 00000000..8f3b9de6 --- /dev/null +++ b/sonar-go-to-slang/goparser_test.go @@ -0,0 +1,114 @@ +// SonarQube Go Plugin +// Copyright (C) 2018-2024 SonarSource SA +// mailto:info AT sonarsource DOT com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +// The following directive is necessary to make the package coherent: + +// This program generates 'goparser_generated.go'. It can be invoked by running "go generate" + +package main + +import ( + "encoding/json" + "fmt" + "go/ast" + "go/token" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "strings" + "testing" +) + +func slangFromString(source string, nodeQueryPath string) (*Node, []*Node, []*Token) { + fileSet, astNode := astFromString(source) + return toSlangTree(fileSet, astNode, source) +} + +func astFromString(source string) (fileSet *token.FileSet, astFile *ast.File) { + fileSet, astFile, err := readAstString("main.go", source) + if err != nil { + panic(err) + } + return +} + +// Update all .txt files in resources/ast from all .go.source files +// Add "Test_" before to run in IDE +func fix_all_go_files_test_automatically(t *testing.T) { + for _, file := range getAllGoFiles("resources/ast") { + source, err := ioutil.ReadFile(file) + if err != nil { + panic(err) + } + + actual := toJsonSlang(slangFromString(string(source), "")) + d1 := []byte(actual) + errWrite := ioutil.WriteFile(strings.Replace(file, "go.source", "json", 1), d1, 0644) + if errWrite != nil { + panic(errWrite) + } + } +} + +func Test_all_go_files(t *testing.T) { + for _, file := range getAllGoFiles("resources/ast") { + source, err := ioutil.ReadFile(file) + if err != nil { + panic(err) + } + actual := toJsonSlang(slangFromString(string(source), "")) + + var jsonActual interface{} + err1 := json.Unmarshal([]byte(actual), &jsonActual) + if err1 != nil { + panic(err1) + } + + expectedData, err := ioutil.ReadFile(strings.Replace(file, "go.source", "json", 1)) + if err != nil { + panic(err) + } + var jsonExpected map[string]interface{} + + err2 := json.Unmarshal(expectedData, &jsonExpected) + if err2 != nil { + panic(err2) + } + + if !reflect.DeepEqual(jsonExpected, jsonActual) { + fmt.Printf("Failed to match expected results for file: %#v\n", file) + t.Fatalf("got: %#v\nexpected: %#v", jsonActual, jsonExpected) + } + } +} + +func getAllGoFiles(folder string) []string { + var files []string + + err := filepath.Walk(folder, func(path string, info os.FileInfo, err error) error { + if strings.HasSuffix(path, ".go.source") { + files = append(files, path) + } + return nil + }) + if err != nil { + panic(err) + } + return files +} diff --git a/sonar-go-to-slang/jsonSlang.go b/sonar-go-to-slang/jsonSlang.go new file mode 100644 index 00000000..7c53841a --- /dev/null +++ b/sonar-go-to-slang/jsonSlang.go @@ -0,0 +1,199 @@ +// SonarQube Go Plugin +// Copyright (C) 2018-2024 SonarSource SA +// mailto:info AT sonarsource DOT com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "sort" + "strings" +) + +func toJsonSlang(node *Node, comments []*Node, tokens []*Token) string { + var buf bytes.Buffer + indent := " " + buf.WriteString("{ \n") + marshallSlangMetaData(&buf, comments, tokens, indent) + buf.WriteString(indent + "\"tree\":\n") + marshalIndentSlang(&buf, node, " ", indent) + buf.WriteString("\n} \n") + return string(buf.Bytes()) +} + +func marshallSlangMetaData(dst *bytes.Buffer, comments []*Node, tokens []*Token, indent string) { + dst.WriteString(indent + "\"treeMetaData\": {\n") + dst.WriteString(strings.Repeat(indent, 2) + "\"comments\": [\n") + + sizeComments := len(comments) + if sizeComments != 0 { + for i := 0; i < sizeComments-1; i++ { + dst.WriteString(strings.Repeat(indent, 3)) + marshallComment(dst, comments[i], strings.Repeat(indent, 3)) + dst.WriteString(",\n") + } + dst.WriteString(strings.Repeat(indent, 3)) + marshallComment(dst, comments[sizeComments-1], strings.Repeat(indent, 3)) + dst.WriteString("\n") + } + + dst.WriteString(strings.Repeat(indent, 2) + "],\n") + + dst.WriteString(strings.Repeat(indent, 2) + "\"tokens\": [\n") + sizeTokens := len(tokens) + if sizeTokens != 0 { + for i := 0; i < sizeTokens-1; i++ { + marshallToken(dst, tokens[i], strings.Repeat(indent, 3)) + dst.WriteString(",\n") + } + marshallToken(dst, tokens[sizeTokens-1], strings.Repeat(indent, 3)) + dst.WriteString("\n") + } + dst.WriteString(strings.Repeat(indent, 2) + "]\n") + + dst.WriteString(indent + "},\n") +} + +func marshallComment(dst *bytes.Buffer, comment *Node, prefix string) { + text := comment.Token.Value + var contentText string + + textRange := comment.TextRange + textContentRange := TextRange(*textRange) + textContentRange.StartColumn = textContentRange.StartColumn + 2 + + if strings.HasPrefix(text, "//") { + contentText = text[2:] + } else if strings.HasPrefix(text, "/*") { + contentText = text[2 : len(text)-2] + textContentRange.EndColumn = textContentRange.EndColumn - 2 + } else { + panic("Unknown comment content: " + text) + } + + dst.WriteString(prefix + "{\"text\":") + writeObjectSlang(dst, text) + dst.WriteString(", \"contentText\":") + writeObjectSlang(dst, contentText) + dst.WriteString(", \"range\":") + writeObjectSlang(dst, textRange) + dst.WriteString(", \"contentRange\": ") + writeObjectSlang(dst, textContentRange) + dst.WriteString("}") +} + +func marshallToken(dst *bytes.Buffer, token *Token, prefix string) { + dst.WriteString(prefix + "{\"text\":") + writeObjectSlang(dst, token.Value) + dst.WriteString(",\"textRange\":") + writeObjectSlang(dst, token.TextRange) + + if token.TokenType != other { + dst.WriteString(",\"type\":") + writeObjectSlang(dst, token.TokenType) + } + dst.WriteString("}") +} + +func marshalIndentSlang(dst *bytes.Buffer, node *Node, prefix, indent string) { + if node == nil { + dst.WriteString(prefix + "null") + return + } + + dst.WriteString(prefix + "{\"@type\": ") + writeObjectSlang(dst, node.SlangType) + + if node.TextRange != nil { + dst.WriteString(", \"metaData\": ") + writeObjectSlang(dst, node.TextRange) + } + + if len(node.SlangField) != 0 { + //Sort the fields to report them in the same order + sortedField := sortSlangField(node.SlangField) + + for _, kv := range sortedField { + if kv.Key == nativeKind && kv.Value == "" { + continue + } + + dst.WriteString(",\"" + kv.Key + "\":") + + switch obj := kv.Value.(type) { + case *Node: + dst.WriteString("\n") + marshalIndentSlang(dst, obj, prefix+indent, indent) + case []*Node: + marshalNodeArray(dst, obj, prefix, indent) + default: + writeObjectSlang(dst, obj) + } + } + } + dst.WriteString("}") +} + +func marshalNodeArray(dst *bytes.Buffer, nodes []*Node, prefix, indent string) { + size := len(nodes) + if size == 0 { + dst.WriteString("[]") + } else { + dst.WriteString("[\n") + for i := 0; i < size-1; i++ { + marshalIndentSlang(dst, nodes[i], prefix+indent, indent) + dst.WriteString(",\n") + } + marshalIndentSlang(dst, nodes[size-1], prefix+indent, indent) + dst.WriteString("\n" + prefix + "]") + } +} + +type KeyValue struct { + Key string + Value interface{} +} + +func sortSlangField(slangField map[string]interface{}) []KeyValue { + var sortedField []KeyValue + for k, v := range slangField { + sortedField = append(sortedField, KeyValue{k, v}) + } + + sort.Slice(sortedField, func(i, j int) bool { + return sortedField[i].Key > sortedField[j].Key + }) + + return sortedField +} + +func writeObjectSlang(dst *bytes.Buffer, obj interface{}) { + b, err := json.Marshal(obj) + if err != nil { + panic(err) + } + + dst.Write(b) +} + +func (t TextRange) MarshalJSON() ([]byte, error) { + if t.StartLine == t.EndLine { + return []byte(fmt.Sprintf("\"%d:%d::%d\"", t.StartLine, t.StartColumn-1, t.EndColumn-1)), nil + } + return []byte(fmt.Sprintf("\"%d:%d:%d:%d\"", t.StartLine, t.StartColumn-1, t.EndLine, t.EndColumn-1)), nil +} diff --git a/sonar-go-to-slang/main.go b/sonar-go-to-slang/main.go new file mode 100644 index 00000000..ded48856 --- /dev/null +++ b/sonar-go-to-slang/main.go @@ -0,0 +1,71 @@ +// SonarQube Go Plugin +// Copyright (C) 2018-2024 SonarSource SA +// mailto:info AT sonarsource DOT com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +package main + +import ( + "flag" + "fmt" + "os" +) + +func exit() { + flag.Usage() + os.Exit(1) +} + +type Params struct { + dumpAst bool + path string +} + +func parseArgs() Params { + flag.Usage = func() { + fmt.Printf("Usage: %s [options] source.go\n\n", os.Args[0]) + flag.PrintDefaults() + } + + dumpAstFlag := flag.Bool("d", false, "dump ast (instead of JSON)") + flag.Parse() + var path string + if len(flag.Args()) == 1 { + path = flag.Args()[0] + } + + return Params{ + dumpAst: *dumpAstFlag, + path: path, + } +} + +func main() { + params := parseArgs() + + //Produce go AST + fileSet, astFile, fileContent, err := readAstFile(params.path) + if err != nil { + panic(err) + } + + if params.dumpAst { + fmt.Println(render(astFile)) + } else { + slangTree, comments, tokens := toSlangTree(fileSet, astFile, fileContent) + fmt.Println(toJsonSlang(slangTree, comments, tokens)) + } +} diff --git a/sonar-go-to-slang/make.sh b/sonar-go-to-slang/make.sh new file mode 100755 index 00000000..7d9b42c5 --- /dev/null +++ b/sonar-go-to-slang/make.sh @@ -0,0 +1,128 @@ +#! /usr/bin/env bash +set -euox pipefail + +readonly GO_VERSION="1.21.1" +readonly DEFAULT_GO_BINARY_DIRECTORY="${GOPATH:=${HOME}/go}/bin" +readonly DEFAULT_GO_BINARY="${DEFAULT_GO_BINARY_DIRECTORY}/go" + +is_go_binary_the_expected_version() { + if [[ "${#}" -ne 2 ]]; then + echo "Usage: is_go_binary_the_expected_version " + exit 1 + fi + local go_binary="${1}" + local expected_version="${2}" + bash -c "${go_binary} version" | grep --quiet "${expected_version}" +} + +go_download_go() { + if [[ "${#}" -ne 2 ]]; then + echo "Usage: go_install_go " + exit 1 + fi + local go_binary="${1}" + local expected_version="${2}" + bash -c "${go_binary} install golang.org/dl/go${go_version}@latest" + go_binary="${DEFAULT_GO_BINARY_DIRECTORY}/go${go_version}" + if [[ ! -f "${go_binary}" ]]; then + if [[ -f "${DEFAULT_GO_BINARY}" ]] && is_go_binary_the_expected_version "${DEFAULT_GO_BINARY}" "${go_version}"; then + go_binary="${DEFAULT_GO_BINARY}" + else + echo "Could not find designated go binary after download" >&2 + exit 1 + fi + fi + bash -c "${go_binary} download" + echo "${go_binary}" +} + +install_go() { + if [[ "${#}" -ne 1 ]]; then + echo "Usage: install_go " >&2 + exit 1 + fi + + local go_version="${1}" + local go_binary + + local go_in_path + go_in_path=$(command -v go) + if [[ -n "${go_in_path}" ]]; then + if is_go_binary_the_expected_version "${go_in_path}" "${go_version}"; then + go_binary="${go_in_path}" + else + go_binary=$(go_download_go "${go_in_path}" "${go_version}") + fi + elif [[ -f "${DEFAULT_GO_BINARY}" ]]; then + if is_go_binary_the_expected_version "${DEFAULT_GO_BINARY}" "${go_version}"; then + go_binary="${DEFAULT_GO_BINARY}" + else + go_binary=$(go_download_go "${DEFAULT_GO_BINARY}" "${go_version}") + fi + else + # Download go + pushd "${HOME}" >&2 + local url="https://dl.google.com/go/go${go_version}.linux-amd64.tar.gz" + curl --request GET "${url}" --output go.linux-amd64.tar.gz --silent + tar xvf go.linux-amd64.tar.gz >/dev/null 2>&1 + if [[ ! -f "${DEFAULT_GO_BINARY}" ]]; then + echo "Could not extract go from archive" >&2 + popd >&2 + exit 2 + fi + popd >&2 + # Set up env variables for go + export PATH="${PATH}:${DEFAULT_GO_BINARY_DIRECTORY}" + go_binary="${DEFAULT_GO_BINARY}" + fi + echo "${go_binary}" +} + +compile_binaries() { + # Install the proper go version + local path_to_binary + path_to_binary=$(install_go "${GO_VERSION}") + # Build + bash -c "${path_to_binary} run generate_source.go" + bash -c "GOOS=darwin GOARCH=amd64 ${path_to_binary} build -o build/sonar-go-to-slang-darwin-amd64" + bash -c "GOOS=darwin GOARCH=arm64 ${path_to_binary} build -o build/sonar-go-to-slang-darwin-arm64" + bash -c "GOOS=linux GOARCH=amd64 ${path_to_binary} build -o build/sonar-go-to-slang-linux-amd64" + bash -c "GOOS=windows GOARCH=amd64 ${path_to_binary} build -o build/sonar-go-to-slang-windows-amd64.exe" +} + +generate_test_report() { + # Install the proper go version + local path_to_binary + path_to_binary=$(install_go "${GO_VERSION}") + # Test + bash -c "${path_to_binary} test -json > test-report.out" +} + + +main() { + if [[ "${#}" -ne 1 ]]; then + echo "Usage: ${0} build | clean | generate-test-report" + exit 0 + fi + local command="${1}" + case "${command}" in + build) + compile_binaries + ;; + generate-test-report) + generate_test_report + ;; + clean) + rm -f goparser_generated.go + rm -f build/sonar-go-to-slang-* + rm -f test-report.out + ;; + *) + echo "Unrecognized command ${command}" >&2 + exit 1 + ;; + esac + exit 0 +} + +main "${@}" diff --git a/sonar-go-to-slang/nodeImpl.go b/sonar-go-to-slang/nodeImpl.go new file mode 100644 index 00000000..17bd5b01 --- /dev/null +++ b/sonar-go-to-slang/nodeImpl.go @@ -0,0 +1,955 @@ +// SonarQube Go Plugin +// Copyright (C) 2018-2024 SonarSource SA +// mailto:info AT sonarsource DOT com +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 3 of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +package main + +import ( + "go/ast" + "go/token" + "strconv" +) + +const keywordField = "keyword" +const modifiersField = "modifiers" +const identifierField = "identifier" +const operatorField = "operator" +const operandField = "operand" +const conditionField = "condition" +const expressionField = "expression" +const lParentKind = "Lparen" +const rParentKind = "Rparen" + +func (t *SlangMapper) mapReturnStmtImpl(stmt *ast.ReturnStmt, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + returnToken := t.createTokenFromPosAstToken(stmt.Return, token.RETURN, "Return") + slangField[keywordField] = returnToken.Token.TextRange + children = t.appendNode(children, returnToken) + + if len(stmt.Results) == 0 { + slangField["body"] = nil + } else if len(stmt.Results) == 1 { + body := t.mapExpr(stmt.Results[0], "["+strconv.Itoa(0)+"]") + slangField["body"] = body + children = t.appendNode(children, body) + } else { + //Slang does not support return with multiple expressions, we wrap it in a native node + var returnBodyList []*Node + for i := 0; i < len(stmt.Results); i++ { + returnBodyList = t.appendNode(returnBodyList, t.mapExpr(stmt.Results[i], "["+strconv.Itoa(i)+"]")) + } + returnExpressions := t.createNativeNodeWithChildren(returnBodyList, "ReturnExprList") + slangField["body"] = returnExpressions + children = t.appendNode(children, returnExpressions) + } + + return t.createNode(stmt, children, fieldName+"(ReturnStmt)", "Return", slangField) +} + +func (t *SlangMapper) mapIdentImpl(ident *ast.Ident, fieldName string) *Node { + slangField := make(map[string]interface{}) + var slangType string + + var children []*Node + switch ident.Name { + case "true", "false", "nil": + slangType = "Literal" + slangField["value"] = ident.Name + case "_": + slangType = "PlaceHolder" + placeHolderToken := t.createExpectedToken(ident.NamePos, "_", "PlaceHolder", "KEYWORD") + children = t.appendNode(children, placeHolderToken) + slangField["placeHolderToken"] = placeHolderToken.TextRange + default: + slangType = "Identifier" + slangField["name"] = ident.Name + } + + return t.createNode(ident, children, fieldName+"(Ident)", slangType, slangField) +} + +func (t *SlangMapper) mapFileImpl(file *ast.File, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + var declarations []*Node + + packageDecl := t.mapPackageDecl(file) + children = t.appendNode(children, packageDecl) + declarations = append(declarations, packageDecl) + + var nodeListDecls []*Node + for i := 0; i < len(file.Decls); i++ { + nodeListDecls = t.appendNode(nodeListDecls, t.mapDecl(file.Decls[i], "["+strconv.Itoa(i)+"]")) + } + children = t.appendNodeList(children, nodeListDecls, "Decls([]Decl)") + declarations = append(declarations, t.filterOutComments(nodeListDecls)...) + + slangField["declarations"] = declarations + slangField["firstCpdToken"] = nil + return t.createNode(file, children, fieldName, "TopLevel", slangField) +} + +func (t *SlangMapper) mapDeclImpl(decl ast.Decl, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapBadDeclImpl(decl *ast.BadDecl, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFuncDeclImpl(decl *ast.FuncDecl, fieldName string) *Node { + var children []*Node + var nativeChildren []*Node + slangField := make(map[string]interface{}) + + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.Type.Func, token.FUNC, "Type.Func")) + + receiver := t.mapFieldListReceiver(decl.Recv, "Recv") + children = t.appendNode(children, receiver) + nativeChildren = t.appendNode(nativeChildren, receiver) + + funcName := t.mapIdent(decl.Name, "Name") + children = t.appendNode(children, funcName) + slangField["name"] = funcName + + // Add type parameters to native children of the function declaration + typeParams := t.mapFieldListTypeParams(decl.Type.TypeParams, "TypeParams") + children = t.appendNode(children, typeParams) + nativeChildren = t.appendNode(nativeChildren, typeParams) + + parameters := t.mapFieldListParams(decl.Type.Params, "Params") + children = t.appendNode(children, parameters) + formalParameters := t.getFormalParameter(parameters) + slangField["formalParameters"] = formalParameters + + funcResults := t.mapFieldListResults(decl.Type.Results, "Results") + children = t.appendNode(children, funcResults) + slangField["returnType"] = funcResults + + funcBody := t.mapBlockStmt(decl.Body, "Body") + children = t.appendNode(children, funcBody) + slangField["body"] = funcBody + + //Required by SLang; Go does not have constructors + slangField["isConstructor"] = false + //Go does not have explicit modifiers + slangField[modifiersField] = nil + //Other children of the function node + slangField["nativeChildren"] = nativeChildren + + return t.createNode(decl, children, fieldName+"(FuncDecl)", "FunctionDeclaration", slangField) +} + +func (t *SlangMapper) mapFuncLitImpl(lit *ast.FuncLit, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + children = t.appendNode(children, t.createTokenFromPosAstToken(lit.Type.Func, token.FUNC, "Type.Func")) + + parameters := t.mapFieldListParams(lit.Type.Params, "Params") + children = t.appendNode(children, parameters) + slangField["formalParameters"] = t.getFormalParameter(parameters) + + funcResults := t.mapFieldListResults(lit.Type.Results, "Results") + children = t.appendNode(children, funcResults) + slangField["returnType"] = funcResults + + funcBody := t.mapBlockStmt(lit.Body, "Body") + children = t.appendNode(children, funcBody) + slangField["body"] = funcBody + + //Required by SLang; Go does not have constructors + slangField["isConstructor"] = false + //Go does not have explicit modifiers + slangField[modifiersField] = nil + //Other children of the function node + slangField["nativeChildren"] = nil + + return t.createNode(lit, children, fieldName+"(FuncLit)", "FunctionDeclaration", slangField) +} + +func (t *SlangMapper) getFormalParameter(node *Node) []*Node { + var formalParameters []*Node + //Get all FieldListParams lists + childrenWithoutComment := t.filterOutComments(node.Children) + for i := 1; i < len(childrenWithoutComment)-1; i++ { + //Get all params inside this list (excluding comma) + currentList := t.filterOutComments(childrenWithoutComment[i].Children) + for j := 0; j < len(currentList); j = j + 2 { + formalParameters = append(formalParameters, currentList[j]) + } + } + return formalParameters +} + +func (t *SlangMapper) mapGenDeclImport(decl *ast.GenDecl, fieldName string) *Node { + var children []*Node + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.TokPos, decl.Tok, "Tok")) + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.Lparen, token.LPAREN, lParentKind)) + + for i := 0; i < len(decl.Specs); i++ { + children = t.appendNode(children, t.mapSpec(decl.Specs[i], "["+strconv.Itoa(i)+"]")) + } + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.Rparen, token.RPAREN, rParentKind)) + + slangField := make(map[string]interface{}) + slangField["children"] = t.filterOutComments(children) + + return t.createNode(decl, children, fieldName+"(ImportSpec)", "ImportDeclaration", slangField) +} + +func (t *SlangMapper) mapGenDeclType(decl *ast.GenDecl, fieldName string) *Node { + if len(decl.Specs) != 1 { + //The node can not be mapped to a typed Slang node, create a native node + return nil + } + + spec, ok := decl.Specs[0].(*ast.TypeSpec) + if !ok { + // The spec of this declaration is not a TypeSpec, we map it to native + return nil + } + + var children []*Node + slangField := make(map[string]interface{}) + + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.TokPos, decl.Tok, "Tok")) + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.Lparen, token.LPAREN, lParentKind)) + + specName := t.mapIdent(spec.Name, "Name") + children = t.appendNode(children, specName) + slangField[identifierField] = specName.TextRange + + children = t.appendNode(children, t.mapFieldListTypeParams(spec.TypeParams, "TypeParams")) + children = t.appendNode(children, t.createTokenFromPosAstToken(spec.Assign, token.ASSIGN, "Assign")) + children = t.appendNode(children, t.mapExpr(spec.Type, "Type")) + + //ClassTree in SLang contains everything (including identifier), we create a new node for this purpose + classTree := t.createNativeNode(spec, children, fieldName+"(TypeSpecWrapped)") + + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.Rparen, token.RPAREN, rParentKind)) + + slangField["classTree"] = classTree + return t.createNode(spec, []*Node{classTree}, fieldName+"(TypeSpec)", "ClassDeclaration", slangField) +} + +func (t *SlangMapper) mapGenDeclImpl(decl *ast.GenDecl, fieldName string) *Node { + slangField := make(map[string]interface{}) + + switch decl.Tok { + case token.CONST: + slangField["isVal"] = true + case token.VAR: + slangField["isVal"] = false + case token.TYPE: + if decl.Lparen == token.NoPos { + // token type with parenthesis has no identifier, we map it to Native + return t.mapGenDeclType(decl, fieldName) + } else { + return nil + } + case token.IMPORT: + return t.mapGenDeclImport(decl, fieldName) + } + + if len(decl.Specs) != 1 { + //The node can not be mapped to a typed Slang node, create a native node + return nil + } + + valueSpec, ok := decl.Specs[0].(*ast.ValueSpec) + if !ok || len(valueSpec.Names) != 1 { + // The spec of this declaration is not a valueSpec, or have multiple identifier (i, j := 1, 2), we map it to native + return nil + } + + var children []*Node + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.TokPos, decl.Tok, "Tok")) + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.Lparen, token.LPAREN, lParentKind)) + + identifier := t.mapIdent(valueSpec.Names[0], "[0]") + children = t.appendNode(children, identifier) + slangField[identifierField] = identifier + + typ := t.mapExpr(valueSpec.Type, "Type") + children = t.appendNode(children, typ) + slangField["type"] = typ + + var initializer *Node + nValues := len(valueSpec.Values) + + if nValues > 1 { + var nodeListValues []*Node + for i := 0; i < len(valueSpec.Values); i++ { + nodeListValues = t.appendNode(nodeListValues, t.mapExpr(valueSpec.Values[i], "["+strconv.Itoa(i)+"]")) + } + //Wrap all values in a native node + initializer = t.createNativeNodeWithChildren(nodeListValues, "Values([]Expr)") + } else if nValues == 1 { + initializer = t.mapExpr(valueSpec.Values[0], "[0]") + } else { + initializer = nil + } + + children = t.appendNode(children, initializer) + slangField["initializer"] = initializer + + children = t.appendNode(children, t.createTokenFromPosAstToken(decl.Rparen, token.RPAREN, rParentKind)) + + return t.createNode(decl, children, fieldName+"(GenDecl)", "VariableDeclaration", slangField) +} + +func (t *SlangMapper) mapFieldListParamsImpl(list *ast.FieldList, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFieldListResultsImpl(list *ast.FieldList, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFieldListReceiverImpl(list *ast.FieldList, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFieldListBraceImpl(list *ast.FieldList, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFuncTypeImpl(funcType *ast.FuncType, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFieldListTypeParamsImpl(list *ast.FieldList, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFuncTypeDeclImpl(funcType *ast.FuncType, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapBlockStmtImpl(blockStmt *ast.BlockStmt, fieldName string) *Node { + var children []*Node + children = t.appendNode(children, t.createTokenFromPosAstToken(blockStmt.Lbrace, token.LBRACE, "Lbrace")) + for i := 0; i < len(blockStmt.List); i++ { + children = t.appendNode(children, t.mapStmt(blockStmt.List[i], "["+strconv.Itoa(i)+"]")) + } + children = t.appendNode(children, t.createTokenFromPosAstToken(blockStmt.Rbrace, token.RBRACE, "Rbrace")) + + slangField := make(map[string]interface{}) + + // children without the braces + slangField["statementOrExpressions"] = t.filterOutComments(children[1 : len(children)-1]) + + return t.createNode(blockStmt, children, fieldName+"(BlockStmt)", "Block", slangField) +} + +func (t *SlangMapper) mapSpecImpl(spec ast.Spec, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFieldImpl(field *ast.Field, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFieldResultImpl(field *ast.Field, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapFieldParamImpl(field *ast.Field, fieldName string) *Node { + var children []*Node + + nNames := len(field.Names) + + if nNames <= 0 { + return nil + } + //Go parameter can share the type with multiple identifier ex: f(a, b int) + //We will create a parameter node without type for the firsts and with type for the last + for i := 0; i < nNames-1; i++ { + paramterIdent := t.mapIdent(field.Names[i], fieldName+"["+strconv.Itoa(i)+"]") + parameter := t.createParameter(field.Names[i], paramterIdent, nil, fieldName) + children = t.appendNode(children, parameter) + } + lastParameterIdent := t.mapIdent(field.Names[nNames-1], fieldName+"["+strconv.Itoa(nNames-1)+"]") + lastParameterType := t.mapExpr(field.Type, "Type") + + lastParameter := t.createParameter(field.Names[nNames-1], lastParameterIdent, lastParameterType, fieldName) + children = t.appendNode(children, lastParameter) + + return t.createNativeNode(field, children, fieldName+"(Field)") +} + +func (t *SlangMapper) createParameter(ident *ast.Ident, parameterIdent, typ *Node, fieldName string) *Node { + slangField := make(map[string]interface{}) + children := []*Node{parameterIdent} + if typ != nil { + children = t.appendNode(children, typ) + } + slangField[identifierField] = parameterIdent + slangField["type"] = typ + slangField[modifiersField] = nil //No parameter modifier in Go + slangField["defaultValue"] = nil //No default value in Go + return t.createNode(ident, children, fieldName+"(Parameter)", "Parameter", slangField) +} + +func (t *SlangMapper) mapStmtImpl(stmt ast.Stmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapImportSpecImpl(spec *ast.ImportSpec, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapTypeSpecImpl(spec *ast.TypeSpec, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapValueSpecImpl(spec *ast.ValueSpec, fieldName string) *Node { + // ValueSpec represents declaration, but they will be mapped inside mapGenDeclImpl to know if the node is a const or not. + return nil +} + +func (t *SlangMapper) mapExprImpl(expr ast.Expr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapAssignStmtImpl(stmt *ast.AssignStmt, fieldName string) *Node { + if stmt.Lhs == nil || stmt.Rhs == nil { + //SLang does not support null LHS or RHS for assignment + return nil + } + + var operator string + var isVarDecl = false + switch stmt.Tok { + case token.ASSIGN: + operator = "EQUAL" + case token.ADD_ASSIGN: + operator = "PLUS_EQUAL" + case token.DEFINE: + if len(stmt.Lhs) != 1 { + //i, j := 1, 2; SLang does not support this, map to Native + return nil + } + isVarDecl = true + default: + // Slang only support = and +=, other compound assignments are ignored. + return nil + } + + var leftHandSide *Node + if len(stmt.Lhs) > 1 { + var nodeListLhs []*Node + for i := 0; i < len(stmt.Lhs); i++ { + nodeListLhs = t.appendNode(nodeListLhs, t.mapExpr(stmt.Lhs[i], "["+strconv.Itoa(i)+"]")) + } + leftHandSide = t.createNativeNodeWithChildren(nodeListLhs, "Lhs([]Expr)") + } else { + leftHandSide = t.mapExpr(stmt.Lhs[0], "[0]") + } + + var children []*Node + children = t.appendNode(children, leftHandSide) + + children = t.appendNode(children, t.createTokenFromPosAstToken(stmt.TokPos, stmt.Tok, "Tok")) + + var rightHandSide *Node + if len(stmt.Rhs) > 1 { + var nodeListRhs []*Node + for i := 0; i < len(stmt.Rhs); i++ { + nodeListRhs = t.appendNode(nodeListRhs, t.mapExpr(stmt.Rhs[i], "["+strconv.Itoa(i)+"]")) + } + rightHandSide = t.createNativeNodeWithChildren(nodeListRhs, "Rhs([]Expr)") + } else { + rightHandSide = t.mapExpr(stmt.Rhs[0], "[0]") + } + children = t.appendNode(children, rightHandSide) + + slangField := make(map[string]interface{}) + if isVarDecl { + slangField[identifierField] = leftHandSide + slangField["type"] = nil + slangField["initializer"] = rightHandSide + slangField["isVal"] = false + return t.createNode(stmt, children, fieldName+"(AssignDefineStmt)", "VariableDeclaration", slangField) + } else { + slangField[operatorField] = operator + slangField["leftHandSide"] = leftHandSide + slangField["statementOrExpression"] = rightHandSide + return t.createNode(stmt, children, fieldName+"(AssignStmt)", "AssignmentExpression", slangField) + } +} + +func (t *SlangMapper) mapBadStmtImpl(stmt *ast.BadStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapBranchStmtImpl(stmt *ast.BranchStmt, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + var jumpKind string + + switch stmt.Tok { + case token.BREAK: + jumpKind = "BREAK" + case token.CONTINUE: + jumpKind = "CONTINUE" + default: + return nil + } + + branchToken := t.createTokenFromPosAstToken(stmt.TokPos, stmt.Tok, "Tok"+jumpKind) + children = t.appendNode(children, branchToken) + slangField[keywordField] = branchToken.TextRange + slangField["kind"] = jumpKind + + label := t.mapIdent(stmt.Label, "Label") + children = t.appendNode(children, label) + slangField["label"] = label + + return t.createNode(stmt, children, fieldName+"(BranchStmt)", "Jump", slangField) +} + +func (t *SlangMapper) mapCaseClauseImpl(clause *ast.CaseClause, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + children = t.handleSwitchCase(clause.Case, len(clause.List) == 0, children) + + var clauseList []*Node + for i := 0; i < len(clause.List); i++ { + clauseList = t.appendNode(clauseList, t.mapExpr(clause.List[i], "["+strconv.Itoa(i)+"]")) + } + //SLang requires a tree as expression and not a list, we wrap it in a native node + caseExpression := t.createNativeNodeWithChildren(clauseList, "CaseExprList") + children = t.appendNode(children, caseExpression) + slangField[expressionField] = caseExpression + + children = t.appendNode(children, t.createTokenFromPosAstToken(clause.Colon, token.COLON, "Colon")) + + var nodeListBody []*Node + for i := 0; i < len(clause.Body); i++ { + nodeListBody = t.appendNode(nodeListBody, t.mapStmt(clause.Body[i], "["+strconv.Itoa(i)+"]")) + } + + //SLang requires a tree as body and not a list, we wrap it in a block + nodeListBodyWithoutComment := t.filterOutComments(nodeListBody) + var caseBody *Node + + if len(nodeListBodyWithoutComment) == 1 && nodeListBodyWithoutComment[0].SlangType == "Block" { + caseBody = nodeListBodyWithoutComment[0] + } else { + slangFieldBlock := make(map[string]interface{}) + slangFieldBlock["statementOrExpressions"] = nodeListBodyWithoutComment + caseBody = t.createNode(nil, nodeListBody, fieldName+"(BlockStmt)", "Block", slangFieldBlock) + } + + children = t.appendNode(children, caseBody) + slangField["body"] = caseBody + + return t.createNode(clause, children, fieldName+"(CaseClause)", "MatchCase", slangField) +} + +func (t *SlangMapper) mapCommClauseImpl(clause *ast.CommClause, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapDeclStmtImpl(stmt *ast.DeclStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapDeferStmtImpl(stmt *ast.DeferStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapEmptyStmtImpl(stmt *ast.EmptyStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapExprStmtImpl(stmt *ast.ExprStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapForStmtImpl(stmt *ast.ForStmt, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + hasInitOrPost := stmt.Init != nil || stmt.Post != nil + + forToken := t.createTokenFromPosAstToken(stmt.For, token.FOR, "For") + children = t.appendNode(children, forToken) + slangField[keywordField] = forToken.TextRange + + var condition *Node + var kind string + + if !hasInitOrPost { + condition = t.mapExpr(stmt.Cond, "Cond") + children = t.appendNode(children, condition) + kind = "WHILE" + } else { + var forHeaderList []*Node + forHeaderList = t.appendNode(forHeaderList, t.mapStmt(stmt.Init, "Init")) + forHeaderList = t.appendNode(forHeaderList, t.mapExpr(stmt.Cond, "Cond")) + forHeaderList = t.appendNode(forHeaderList, t.mapStmt(stmt.Post, "Post")) + + //Wrap the 3 elements of the for loop header into one single node + condition = t.createNativeNodeWithChildren(forHeaderList, "ForHeader") + children = t.appendNode(children, condition) + kind = "FOR" + } + slangField[conditionField] = condition + slangField["kind"] = kind + + body := t.mapBlockStmt(stmt.Body, "Body") + children = t.appendNode(children, body) + slangField["body"] = body + + return t.createNode(stmt, children, fieldName+"(ForStmt)", "Loop", slangField) +} + +func (t *SlangMapper) mapGoStmtImpl(stmt *ast.GoStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapIfStmtImpl(ifStmt *ast.IfStmt, fieldName string) *Node { + var children []*Node + ifToken := t.createTokenFromPosAstToken(ifStmt.If, token.IF, "If") + children = t.appendNode(children, ifToken) + + var condition *Node + if ifStmt.Init != nil { + condition = t.createAdditionalInitAndCond(ifStmt.Init, ifStmt.Cond) + } else { + condition = t.mapExpr(ifStmt.Cond, "Cond") + } + + children = t.appendNode(children, condition) + + thenBranch := t.mapBlockStmt(ifStmt.Body, "Body") + children = t.appendNode(children, thenBranch) + + elseBranch := t.mapStmt(ifStmt.Else, "Else") + children = t.appendNode(children, elseBranch) + + slangField := make(map[string]interface{}) + + slangField["ifKeyword"] = ifToken.TextRange + slangField[conditionField] = condition + slangField["thenBranch"] = thenBranch + slangField["elseKeyword"] = nil + slangField["elseBranch"] = elseBranch + + childrenWithoutComments := t.filterOutComments(children) + if elseBranch != nil { + for i := len(childrenWithoutComments) - 1; i >= 0; i-- { + if childrenWithoutComments[i] == elseBranch { + // else keyword is necessarily before, and has been added to the children when calling "appendNode" + slangField["elseKeyword"] = childrenWithoutComments[i-1].TextRange + break + } + } + } + + return t.createNode(ifStmt, children, fieldName+"(IfStmt)", "If", slangField) +} + +func (t *SlangMapper) mapIncDecStmtImpl(stmt *ast.IncDecStmt, fieldName string) *Node { + var operatorName = "DECREMENT" + if token.INC == stmt.Tok { + operatorName = "INCREMENT" + } + + var children []*Node + slangField := make(map[string]interface{}) + + operand := t.mapExpr(stmt.X, "X") + children = t.appendNode(children, operand) + slangField[operandField] = operand + + operator := t.createTokenFromPosAstToken(stmt.TokPos, stmt.Tok, "Tok") + children = t.appendNode(children, operator) + slangField[operatorField] = operatorName + + return t.createNode(stmt, children, fieldName+"(UnaryExpression)", "UnaryExpression", slangField) +} + +func (t *SlangMapper) mapLabeledStmtImpl(stmt *ast.LabeledStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapRangeStmtImpl(stmt *ast.RangeStmt, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + forToken := t.createTokenFromPosAstToken(stmt.For, token.FOR, "For") + children = t.appendNode(children, forToken) + slangField[keywordField] = forToken.TextRange + + var rangeHeaderList []*Node + + rangeHeaderList = t.appendNode(rangeHeaderList, t.mapExpr(stmt.Key, "Key")) + rangeHeaderList = t.appendNode(rangeHeaderList, t.mapExpr(stmt.Value, "Value")) + rangeHeaderList = t.appendNode(rangeHeaderList, t.createTokenFromPosAstToken(stmt.TokPos, stmt.Tok, "Tok")) + rangeHeaderList = t.appendNode(rangeHeaderList, t.mapExpr(stmt.X, "X")) + + //Wrap all element of the range loop into one single node + condition := t.createNativeNodeWithChildren(rangeHeaderList, "RangeHeader") + children = t.appendNode(children, condition) + slangField[conditionField] = condition + + body := t.mapBlockStmt(stmt.Body, "Body") + children = t.appendNode(children, body) + slangField["body"] = body + + slangField["kind"] = "FOR" + + return t.createNode(stmt, children, fieldName+"(RangeStmt)", "Loop", slangField) +} + +func (t *SlangMapper) mapSelectStmtImpl(stmt *ast.SelectStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapSendStmtImpl(stmt *ast.SendStmt, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapSwitchStmtImpl(stmt *ast.SwitchStmt, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + keywordToken := t.createTokenFromPosAstToken(stmt.Switch, token.SWITCH, "Switch") + children = t.appendNode(children, keywordToken) + slangField[keywordField] = keywordToken.TextRange + + var expressionList []*Node + expressionList = t.appendNode(expressionList, t.mapStmt(stmt.Init, "Init")) + expressionList = t.appendNode(expressionList, t.mapExpr(stmt.Tag, "Tag")) + + //Wrap the tag and init into one native node + expression := t.createNativeNodeWithChildren(expressionList, "InitAndTag") + children = t.appendNode(children, expression) + slangField[expressionField] = expression + + body := t.mapBlockStmt(stmt.Body, "Body") + children = t.appendNode(children, body) + slangField["cases"] = t.getMatchCases(body) + + return t.createNode(stmt, children, fieldName+"(SwitchStmt)", "Match", slangField) +} + +func (t *SlangMapper) mapTypeSwitchStmtImpl(stmt *ast.TypeSwitchStmt, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + keywordToken := t.createTokenFromPosAstToken(stmt.Switch, token.SWITCH, "Switch") + children = t.appendNode(children, keywordToken) + slangField[keywordField] = keywordToken.TextRange + + var expressionList []*Node + expressionList = t.appendNode(expressionList, t.mapStmt(stmt.Init, "Init")) + expressionList = t.appendNode(expressionList, t.mapStmt(stmt.Assign, "Assign")) + + //Wrap the init and Assign into one native node + expression := t.createNativeNodeWithChildren(expressionList, "InitAndAssign") + children = t.appendNode(children, expression) + slangField[expressionField] = expression + + body := t.mapBlockStmt(stmt.Body, "Body") + children = t.appendNode(children, body) + slangField["cases"] = t.getMatchCases(body) + + return t.createNode(stmt, children, fieldName+"(TypeSwitchStmt)", "Match", slangField) +} + +func (t *SlangMapper) getMatchCases(node *Node) []*Node { + bodyWithoutComment := t.filterOutComments(node.Children) + var matchCases []*Node + for _, child := range bodyWithoutComment { + if child.SlangType == "MatchCase" { + matchCases = append(matchCases, child) + } + } + return matchCases +} + +func (t *SlangMapper) mapArrayTypeImpl(arrayType *ast.ArrayType, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapBadExprImpl(expr *ast.BadExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapBinaryExprImpl(expr *ast.BinaryExpr, fieldName string) *Node { + + var operatorName = "" + switch expr.Op { + case token.ADD: + operatorName = "PLUS" + case token.SUB: + operatorName = "MINUS" + case token.MUL: + operatorName = "TIMES" + case token.QUO: + operatorName = "DIVIDED_BY" + case token.EQL: + operatorName = "EQUAL_TO" + case token.NEQ: + operatorName = "NOT_EQUAL_TO" + case token.GTR: + operatorName = "GREATER_THAN" + case token.GEQ: + operatorName = "GREATER_THAN_OR_EQUAL_TO" + case token.LSS: + operatorName = "LESS_THAN" + case token.LEQ: + operatorName = "LESS_THAN_OR_EQUAL_TO" + case token.LAND: + operatorName = "CONDITIONAL_AND" + case token.LOR: + operatorName = "CONDITIONAL_OR" + default: + // all the other binary operators are not mapped + return nil + + } + + var children []*Node + slangField := make(map[string]interface{}) + + leftOperand := t.mapExpr(expr.X, operandField) + children = t.appendNode(children, leftOperand) + slangField["leftOperand"] = leftOperand + + operator := t.createTokenFromPosAstToken(expr.OpPos, expr.Op, "Op") + children = t.appendNode(children, operator) + slangField[operatorField] = operatorName + slangField["operatorToken"] = operator.TextRange + + rightOperand := t.mapExpr(expr.Y, operandField) + children = t.appendNode(children, rightOperand) + slangField["rightOperand"] = rightOperand + + return t.createNode(expr, children, fieldName+"(BinaryExpr)", "BinaryExpression", slangField) +} + +func (t *SlangMapper) mapCallExprImpl(expr *ast.CallExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapChanTypeImpl(chanType *ast.ChanType, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapCompositeLitImpl(lit *ast.CompositeLit, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapEllipsisImpl(ellipsis *ast.Ellipsis, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapIndexExprImpl(expr *ast.IndexExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapIndexListExprImpl(astNode *ast.IndexListExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapInterfaceTypeImpl(interfaceType *ast.InterfaceType, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapKeyValueExprImpl(expr *ast.KeyValueExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapMapTypeImpl(mapType *ast.MapType, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapParenExprImpl(expr *ast.ParenExpr, fieldName string) *Node { + var children []*Node + slangField := make(map[string]interface{}) + + leftParen := t.createTokenFromPosAstToken(expr.Lparen, token.LPAREN, lParentKind) + slangField["leftParenthesis"] = leftParen.TextRange + children = t.appendNode(children, leftParen) + + nestedExpr := t.mapExpr(expr.X, "X") + slangField[expressionField] = nestedExpr + children = t.appendNode(children, nestedExpr) + + rightParen := t.createTokenFromPosAstToken(expr.Rparen, token.RPAREN, rParentKind) + children = t.appendNode(children, rightParen) + slangField["rightParenthesis"] = rightParen.TextRange + + return t.createNode(expr, children, fieldName+"(ParenExpr)", "ParenthesizedExpression", slangField) +} + +func (t *SlangMapper) mapSelectorExprImpl(expr *ast.SelectorExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapSliceExprImpl(expr *ast.SliceExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapStarExprImpl(expr *ast.StarExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapStructTypeImpl(structType *ast.StructType, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapTypeAssertExprImpl(expr *ast.TypeAssertExpr, fieldName string) *Node { + return nil +} + +func (t *SlangMapper) mapUnaryExprImpl(expr *ast.UnaryExpr, fieldName string) *Node { + var operatorName = "" + switch expr.Op { + case token.ADD: + operatorName = "PLUS" + case token.SUB: + operatorName = "MINUS" + case token.NOT: + operatorName = "NEGATE" + default: + // only covering unary operators which are supported by SLang + return nil + } + + var children []*Node + slangField := make(map[string]interface{}) + + operator := t.createTokenFromPosAstToken(expr.OpPos, expr.Op, "Op") + children = t.appendNode(children, operator) + slangField[operatorField] = operatorName + + operand := t.mapExpr(expr.X, "X") + children = t.appendNode(children, operand) + slangField[operandField] = operand + + return t.createNode(expr, children, fieldName+"(UnaryExpression)", "UnaryExpression", slangField) +} diff --git a/sonar-go-to-slang/render.go b/sonar-go-to-slang/render.go new file mode 100644 index 00000000..b324b1cb --- /dev/null +++ b/sonar-go-to-slang/render.go @@ -0,0 +1,487 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +package main + +import ( + "bytes" + "fmt" + "reflect" + "sort" + "strconv" + "strings" +) + +var builtinTypeMap = map[reflect.Kind]string{ + reflect.Bool: "bool", + reflect.Complex128: "complex128", + reflect.Complex64: "complex64", + reflect.Float32: "float32", + reflect.Float64: "float64", + reflect.Int16: "int16", + reflect.Int32: "int32", + reflect.Int64: "int64", + reflect.Int8: "int8", + reflect.Int: "int", + reflect.String: "string", + reflect.Uint16: "uint16", + reflect.Uint32: "uint32", + reflect.Uint64: "uint64", + reflect.Uint8: "uint8", + reflect.Uint: "uint", + reflect.Uintptr: "uintptr", +} + +var builtinTypeSet = map[string]struct{}{} + +func init() { + for _, v := range builtinTypeMap { + builtinTypeSet[v] = struct{}{} + } +} + +var typeOfString = reflect.TypeOf("") +var typeOfInt = reflect.TypeOf(int(1)) +var typeOfUint = reflect.TypeOf(uint(1)) +var typeOfFloat = reflect.TypeOf(10.1) + +// render converts a structure to a string representation. Unline the "%#v" +// format string, this resolves pointer types' contents in structs, maps, and +// slices/arrays and prints their field values. +func render(v interface{}) string { + buf := bytes.Buffer{} + s := (*traverseState)(nil) + s.render(&buf, 0, reflect.ValueOf(v), false, 0) + return buf.String() +} + +// renderPointer is called to render a pointer value. +// +// This is overridable so that the test suite can have deterministic pointer +// values in its expectations. +var renderPointer = func(buf *bytes.Buffer, p uintptr) { + fmt.Fprintf(buf, "0x%016x", p) +} + +// traverseState is used to note and avoid recursion as struct members are being +// traversed. +// +// traverseState is allowed to be nil. Specifically, the root state is nil. +type traverseState struct { + parent *traverseState + ptr uintptr +} + +func (s *traverseState) forkFor(ptr uintptr) *traverseState { + for cur := s; cur != nil; cur = cur.parent { + if ptr == cur.ptr { + return nil + } + } + + fs := &traverseState{ + parent: s, + ptr: ptr, + } + return fs +} + +func (s *traverseState) render(buf *bytes.Buffer, ptrs int, v reflect.Value, implicit bool, indent int) { + if v.Kind() == reflect.Invalid { + buf.WriteString("nil") + return + } + vt := v.Type() + + // If the type being rendered is a potentially recursive type (a type that + // can contain itself as a member), we need to avoid recursion. + // + // If we've already seen this type before, mark that this is the case and + // write a recursion placeholder instead of actually rendering it. + // + // If we haven't seen it before, fork our `seen` tracking so any higher-up + // renderers will also render it at least once, then mark that we've seen it + // to avoid recursing on lower layers. + pe := uintptr(0) + vk := vt.Kind() + switch vk { + case reflect.Ptr: + // Since structs and arrays aren't pointers, they can't directly be + // recursed, but they can contain pointers to themselves. Record their + // pointer to avoid this. + switch v.Elem().Kind() { + case reflect.Struct, reflect.Array: + pe = v.Pointer() + } + + case reflect.Slice, reflect.Map: + pe = v.Pointer() + } + if pe != 0 { + s = s.forkFor(pe) + if s == nil { + buf.WriteString("") + return + } + } + + isAnon := func(t reflect.Type) bool { + if t.Name() != "" { + if _, ok := builtinTypeSet[t.Name()]; !ok { + return false + } + } + return t.Kind() != reflect.Interface + } + + switch vk { + case reflect.Struct: + if !implicit { + writeType(buf, ptrs, vt) + } + structAnon := vt.Name() == "" + buf.WriteString(" {\n") + for i := 0; i < vt.NumField(); i++ { + if i > 0 { + buf.WriteString(",\n") + } + anon := structAnon && isAnon(vt.Field(i).Type) + + if !anon { + buf.WriteString(strings.Repeat(" ", indent+1)) + buf.WriteString(vt.Field(i).Name) + buf.WriteString(": ") + } else { + buf.WriteString(strings.Repeat(" ", indent+1)) + } + + s.render(buf, 0, v.Field(i), anon, indent+2) + } + buf.WriteString("\n") + buf.WriteString(strings.Repeat(" ", indent)) + buf.WriteString("}") + + case reflect.Slice: + if v.IsNil() { + if !implicit { + writeType(buf, ptrs, vt) + buf.WriteString("(nil)") + } else { + buf.WriteString("nil") + } + return + } + fallthrough + + case reflect.Array: + if !implicit { + writeType(buf, ptrs, vt) + } + anon := vt.Name() == "" && isAnon(vt.Elem()) + buf.WriteString("{\n") + for i := 0; i < v.Len(); i++ { + if i > 0 { + buf.WriteString(",\n") + } + buf.WriteString(strings.Repeat(" ", indent)) + s.render(buf, 0, v.Index(i), anon, indent+1) + } + buf.WriteString("\n") + buf.WriteString(strings.Repeat(" ", indent)) + buf.WriteString("}") + + case reflect.Map: + if !implicit { + writeType(buf, ptrs, vt) + } + if v.IsNil() { + buf.WriteString("(nil)") + } else { + buf.WriteString("{\n") + + mkeys := v.MapKeys() + tryAndSortMapKeys(vt, mkeys) + + kt := vt.Key() + keyAnon := typeOfString.ConvertibleTo(kt) || typeOfInt.ConvertibleTo(kt) || typeOfUint.ConvertibleTo(kt) || typeOfFloat.ConvertibleTo(kt) + valAnon := vt.Name() == "" && isAnon(vt.Elem()) + for i, mk := range mkeys { + if i > 0 { + buf.WriteString(",\n") + } + buf.WriteString(strings.Repeat(" ", indent)) + s.render(buf, 0, mk, keyAnon, indent+1) + buf.WriteString(": ") + s.render(buf, 0, v.MapIndex(mk), valAnon, indent+2) + } + buf.WriteString("\n") + buf.WriteString(strings.Repeat(" ", indent)) + buf.WriteString("}") + } + + case reflect.Ptr: + ptrs++ + fallthrough + case reflect.Interface: + if v.IsNil() { + writeType(buf, ptrs, v.Type()) + buf.WriteString("(nil)") + } else { + s.render(buf, ptrs, v.Elem(), false, indent) + } + + case reflect.Chan, reflect.Func, reflect.UnsafePointer: + writeType(buf, ptrs, vt) + buf.WriteRune('(') + renderPointer(buf, v.Pointer()) + buf.WriteRune(')') + + default: + tstr := vt.String() + implicit = implicit || (ptrs == 0 && builtinTypeMap[vk] == tstr) + if !implicit { + writeType(buf, ptrs, vt) + buf.WriteRune('(') + } + + switch vk { + case reflect.String: + fmt.Fprintf(buf, "%q", v.String()) + case reflect.Bool: + fmt.Fprintf(buf, "%v", v.Bool()) + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + fmt.Fprintf(buf, "%d", v.Int()) + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + fmt.Fprintf(buf, "%d", v.Uint()) + + case reflect.Float32, reflect.Float64: + fmt.Fprintf(buf, "%g", v.Float()) + + case reflect.Complex64, reflect.Complex128: + fmt.Fprintf(buf, "%g", v.Complex()) + } + + if !implicit { + buf.WriteRune(')') + } + } +} + +func writeType(buf *bytes.Buffer, ptrs int, t reflect.Type) { + parens := ptrs > 0 + switch t.Kind() { + case reflect.Chan, reflect.Func, reflect.UnsafePointer: + parens = true + } + + if parens { + buf.WriteRune('(') + for i := 0; i < ptrs; i++ { + buf.WriteRune('*') + } + } + + switch t.Kind() { + case reflect.Ptr: + if ptrs == 0 { + // This pointer was referenced from within writeType (e.g., as part of + // rendering a list), and so hasn't had its pointer asterisk accounted + // for. + buf.WriteRune('*') + } + writeType(buf, 0, t.Elem()) + + case reflect.Interface: + if n := t.Name(); n != "" { + buf.WriteString(t.String()) + } else { + buf.WriteString("interface{}") + } + + case reflect.Array: + buf.WriteRune('[') + buf.WriteString(strconv.FormatInt(int64(t.Len()), 10)) + buf.WriteRune(']') + writeType(buf, 0, t.Elem()) + + case reflect.Slice: + if t == reflect.SliceOf(t.Elem()) { + buf.WriteString("[]") + writeType(buf, 0, t.Elem()) + } else { + // Custom slice type, use type name. + buf.WriteString(t.String()) + } + + case reflect.Map: + if t == reflect.MapOf(t.Key(), t.Elem()) { + buf.WriteString("map[") + writeType(buf, 0, t.Key()) + buf.WriteRune(']') + writeType(buf, 0, t.Elem()) + } else { + // Custom map type, use type name. + buf.WriteString(t.String()) + } + + default: + buf.WriteString(t.String()) + } + + if parens { + buf.WriteRune(')') + } +} + +type cmpFn func(a, b reflect.Value) int + +type sortableValueSlice struct { + cmp cmpFn + elements []reflect.Value +} + +func (s sortableValueSlice) Len() int { + return len(s.elements) +} + +func (s sortableValueSlice) Less(i, j int) bool { + return s.cmp(s.elements[i], s.elements[j]) < 0 +} + +func (s sortableValueSlice) Swap(i, j int) { + s.elements[i], s.elements[j] = s.elements[j], s.elements[i] +} + +// cmpForType returns a cmpFn which sorts the data for some type t in the same +// order that a go-native map key is compared for equality. +func cmpForType(t reflect.Type) cmpFn { + switch t.Kind() { + case reflect.String: + return func(av, bv reflect.Value) int { + a, b := av.String(), bv.String() + if a < b { + return -1 + } else if a > b { + return 1 + } + return 0 + } + + case reflect.Bool: + return func(av, bv reflect.Value) int { + a, b := av.Bool(), bv.Bool() + if !a && b { + return -1 + } else if a && !b { + return 1 + } + return 0 + } + + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return func(av, bv reflect.Value) int { + a, b := av.Int(), bv.Int() + if a < b { + return -1 + } else if a > b { + return 1 + } + return 0 + } + + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, + reflect.Uint64, reflect.Uintptr, reflect.UnsafePointer: + return func(av, bv reflect.Value) int { + a, b := av.Uint(), bv.Uint() + if a < b { + return -1 + } else if a > b { + return 1 + } + return 0 + } + + case reflect.Float32, reflect.Float64: + return func(av, bv reflect.Value) int { + a, b := av.Float(), bv.Float() + if a < b { + return -1 + } else if a > b { + return 1 + } + return 0 + } + + case reflect.Interface: + return func(av, bv reflect.Value) int { + a, b := av.InterfaceData(), bv.InterfaceData() + if a[0] < b[0] { + return -1 + } else if a[0] > b[0] { + return 1 + } + if a[1] < b[1] { + return -1 + } else if a[1] > b[1] { + return 1 + } + return 0 + } + + case reflect.Complex64, reflect.Complex128: + return func(av, bv reflect.Value) int { + a, b := av.Complex(), bv.Complex() + if real(a) < real(b) { + return -1 + } else if real(a) > real(b) { + return 1 + } + if imag(a) < imag(b) { + return -1 + } else if imag(a) > imag(b) { + return 1 + } + return 0 + } + + case reflect.Ptr, reflect.Chan: + return func(av, bv reflect.Value) int { + a, b := av.Pointer(), bv.Pointer() + if a < b { + return -1 + } else if a > b { + return 1 + } + return 0 + } + + case reflect.Struct: + cmpLst := make([]cmpFn, t.NumField()) + for i := range cmpLst { + cmpLst[i] = cmpForType(t.Field(i).Type) + } + return func(a, b reflect.Value) int { + for i, cmp := range cmpLst { + if rslt := cmp(a.Field(i), b.Field(i)); rslt != 0 { + return rslt + } + } + return 0 + } + } + + return nil +} + +func tryAndSortMapKeys(mt reflect.Type, k []reflect.Value) { + if cmp := cmpForType(mt.Key()); cmp != nil { + sort.Sort(sortableValueSlice{cmp, k}) + } +} diff --git a/sonar-go-to-slang/resources/ast/array_slice.go.source b/sonar-go-to-slang/resources/ast/array_slice.go.source new file mode 100644 index 00000000..3c27af19 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/array_slice.go.source @@ -0,0 +1,23 @@ +package ast + +func allLiterals() { + s := make([]byte, 2, 4) + + a0 := [0]byte(s) + a1 := [1]byte(s[1:]) // a1[0] == s[1] + a2 := [2]byte(s) // a2[0] == s[0] + a4 := [4]byte(s) // panics: len([4]byte) > len(s) + + s0 := (*[0]byte)(s) // s0 != nil + s1 := (*[1]byte)(s[1:]) // &s1[0] == &s[1] + s2 := (*[2]byte)(s) // &s2[0] == &s[0] + s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s) + + var t []string + t0 := [0]string(t) // ok for nil slice t + t1 := (*[0]string)(t) // t1 == nil + t2 := (*[1]string)(t) // panics: len([1]string) > len(t) + + u := make([]byte, 0) + u0 := (*[0]byte)(u) // u0 != nil +} diff --git a/sonar-go-to-slang/resources/ast/array_slice.json b/sonar-go-to-slang/resources/ast/array_slice.json new file mode 100644 index 00000000..f409357f --- /dev/null +++ b/sonar-go-to-slang/resources/ast/array_slice.json @@ -0,0 +1,467 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// a1[0] == s[1]", "contentText":" a1[0] == s[1]", "range":"7:29::45", "contentRange": "7:31::45"}, + {"text":"// a2[0] == s[0]", "contentText":" a2[0] == s[0]", "range":"8:29::45", "contentRange": "8:31::45"}, + {"text":"// panics: len([4]byte) \u003e len(s)", "contentText":" panics: len([4]byte) \u003e len(s)", "range":"9:29::61", "contentRange": "9:31::61"}, + {"text":"// s0 != nil", "contentText":" s0 != nil", "range":"11:29::41", "contentRange": "11:31::41"}, + {"text":"// \u0026s1[0] == \u0026s[1]", "contentText":" \u0026s1[0] == \u0026s[1]", "range":"12:29::47", "contentRange": "12:31::47"}, + {"text":"// \u0026s2[0] == \u0026s[0]", "contentText":" \u0026s2[0] == \u0026s[0]", "range":"13:29::47", "contentRange": "13:31::47"}, + {"text":"// panics: len([4]byte) \u003e len(s)", "contentText":" panics: len([4]byte) \u003e len(s)", "range":"14:29::61", "contentRange": "14:31::61"}, + {"text":"// ok for nil slice t", "contentText":" ok for nil slice t", "range":"17:29::50", "contentRange": "17:31::50"}, + {"text":"// t1 == nil", "contentText":" t1 == nil", "range":"18:29::41", "contentRange": "18:31::41"}, + {"text":"// panics: len([1]string) \u003e len(t)", "contentText":" panics: len([1]string) \u003e len(t)", "range":"19:29::63", "contentRange": "19:31::63"}, + {"text":"// u0 != nil", "contentText":" u0 != nil", "range":"22:29::41", "contentRange": "22:31::41"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"}, + {"text":"func","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"allLiterals","textRange":"3:5::16"}, + {"text":"(","textRange":"3:16::17"}, + {"text":")","textRange":"3:17::18"}, + {"text":"{","textRange":"3:19::20"}, + {"text":"s","textRange":"4:1::2"}, + {"text":":=","textRange":"4:3::5"}, + {"text":"make","textRange":"4:6::10"}, + {"text":"(","textRange":"4:10::11"}, + {"text":"[","textRange":"4:11::12"}, + {"text":"byte","textRange":"4:13::17"}, + {"text":"]","textRange":"4:12::13"}, + {"text":"2","textRange":"4:19::20"}, + {"text":",","textRange":"4:17::18"}, + {"text":"4","textRange":"4:22::23"}, + {"text":",","textRange":"4:20::21"}, + {"text":")","textRange":"4:23::24"}, + {"text":"a0","textRange":"6:4::6"}, + {"text":":=","textRange":"6:7::9"}, + {"text":"[","textRange":"6:10::11"}, + {"text":"0","textRange":"6:11::12"}, + {"text":"byte","textRange":"6:13::17"}, + {"text":"]","textRange":"6:12::13"}, + {"text":"(","textRange":"6:17::18"}, + {"text":"s","textRange":"6:18::19"}, + {"text":")","textRange":"6:19::20"}, + {"text":"a1","textRange":"7:4::6"}, + {"text":":=","textRange":"7:7::9"}, + {"text":"[","textRange":"7:10::11"}, + {"text":"1","textRange":"7:11::12"}, + {"text":"byte","textRange":"7:13::17"}, + {"text":"]","textRange":"7:12::13"}, + {"text":"(","textRange":"7:17::18"}, + {"text":"s","textRange":"7:18::19"}, + {"text":"[","textRange":"7:19::20"}, + {"text":"1","textRange":"7:20::21"}, + {"text":"]","textRange":"7:22::23"}, + {"text":":","textRange":"7:21::22"}, + {"text":")","textRange":"7:23::24"}, + {"text":"a2","textRange":"8:4::6"}, + {"text":":=","textRange":"8:7::9"}, + {"text":"[","textRange":"8:10::11"}, + {"text":"2","textRange":"8:11::12"}, + {"text":"byte","textRange":"8:13::17"}, + {"text":"]","textRange":"8:12::13"}, + {"text":"(","textRange":"8:17::18"}, + {"text":"s","textRange":"8:18::19"}, + {"text":")","textRange":"8:19::20"}, + {"text":"a4","textRange":"9:4::6"}, + {"text":":=","textRange":"9:7::9"}, + {"text":"[","textRange":"9:10::11"}, + {"text":"4","textRange":"9:11::12"}, + {"text":"byte","textRange":"9:13::17"}, + {"text":"]","textRange":"9:12::13"}, + {"text":"(","textRange":"9:17::18"}, + {"text":"s","textRange":"9:18::19"}, + {"text":")","textRange":"9:19::20"}, + {"text":"s0","textRange":"11:4::6"}, + {"text":":=","textRange":"11:7::9"}, + {"text":"(","textRange":"11:10::11"}, + {"text":"*","textRange":"11:11::12"}, + {"text":"[","textRange":"11:12::13"}, + {"text":"0","textRange":"11:13::14"}, + {"text":"byte","textRange":"11:15::19"}, + {"text":"]","textRange":"11:14::15"}, + {"text":")","textRange":"11:19::20"}, + {"text":"(","textRange":"11:20::21"}, + {"text":"s","textRange":"11:21::22"}, + {"text":")","textRange":"11:22::23"}, + {"text":"s1","textRange":"12:4::6"}, + {"text":":=","textRange":"12:7::9"}, + {"text":"(","textRange":"12:10::11"}, + {"text":"*","textRange":"12:11::12"}, + {"text":"[","textRange":"12:12::13"}, + {"text":"1","textRange":"12:13::14"}, + {"text":"byte","textRange":"12:15::19"}, + {"text":"]","textRange":"12:14::15"}, + {"text":")","textRange":"12:19::20"}, + {"text":"(","textRange":"12:20::21"}, + {"text":"s","textRange":"12:21::22"}, + {"text":"[","textRange":"12:22::23"}, + {"text":"1","textRange":"12:23::24"}, + {"text":"]","textRange":"12:25::26"}, + {"text":":","textRange":"12:24::25"}, + {"text":")","textRange":"12:26::27"}, + {"text":"s2","textRange":"13:4::6"}, + {"text":":=","textRange":"13:7::9"}, + {"text":"(","textRange":"13:10::11"}, + {"text":"*","textRange":"13:11::12"}, + {"text":"[","textRange":"13:12::13"}, + {"text":"2","textRange":"13:13::14"}, + {"text":"byte","textRange":"13:15::19"}, + {"text":"]","textRange":"13:14::15"}, + {"text":")","textRange":"13:19::20"}, + {"text":"(","textRange":"13:20::21"}, + {"text":"s","textRange":"13:21::22"}, + {"text":")","textRange":"13:22::23"}, + {"text":"s4","textRange":"14:4::6"}, + {"text":":=","textRange":"14:7::9"}, + {"text":"(","textRange":"14:10::11"}, + {"text":"*","textRange":"14:11::12"}, + {"text":"[","textRange":"14:12::13"}, + {"text":"4","textRange":"14:13::14"}, + {"text":"byte","textRange":"14:15::19"}, + {"text":"]","textRange":"14:14::15"}, + {"text":")","textRange":"14:19::20"}, + {"text":"(","textRange":"14:20::21"}, + {"text":"s","textRange":"14:21::22"}, + {"text":")","textRange":"14:22::23"}, + {"text":"var","textRange":"16:4::7","type":"KEYWORD"}, + {"text":"t","textRange":"16:8::9"}, + {"text":"[","textRange":"16:10::11"}, + {"text":"string","textRange":"16:12::18"}, + {"text":"]","textRange":"16:11::12"}, + {"text":"t0","textRange":"17:4::6"}, + {"text":":=","textRange":"17:7::9"}, + {"text":"[","textRange":"17:10::11"}, + {"text":"0","textRange":"17:11::12"}, + {"text":"string","textRange":"17:13::19"}, + {"text":"]","textRange":"17:12::13"}, + {"text":"(","textRange":"17:19::20"}, + {"text":"t","textRange":"17:20::21"}, + {"text":")","textRange":"17:21::22"}, + {"text":"t1","textRange":"18:4::6"}, + {"text":":=","textRange":"18:7::9"}, + {"text":"(","textRange":"18:10::11"}, + {"text":"*","textRange":"18:11::12"}, + {"text":"[","textRange":"18:12::13"}, + {"text":"0","textRange":"18:13::14"}, + {"text":"string","textRange":"18:15::21"}, + {"text":"]","textRange":"18:14::15"}, + {"text":")","textRange":"18:21::22"}, + {"text":"(","textRange":"18:22::23"}, + {"text":"t","textRange":"18:23::24"}, + {"text":")","textRange":"18:24::25"}, + {"text":"t2","textRange":"19:4::6"}, + {"text":":=","textRange":"19:7::9"}, + {"text":"(","textRange":"19:10::11"}, + {"text":"*","textRange":"19:11::12"}, + {"text":"[","textRange":"19:12::13"}, + {"text":"1","textRange":"19:13::14"}, + {"text":"string","textRange":"19:15::21"}, + {"text":"]","textRange":"19:14::15"}, + {"text":")","textRange":"19:21::22"}, + {"text":"(","textRange":"19:22::23"}, + {"text":"t","textRange":"19:23::24"}, + {"text":")","textRange":"19:24::25"}, + {"text":"u","textRange":"21:4::5"}, + {"text":":=","textRange":"21:6::8"}, + {"text":"make","textRange":"21:9::13"}, + {"text":"(","textRange":"21:13::14"}, + {"text":"[","textRange":"21:14::15"}, + {"text":"byte","textRange":"21:16::20"}, + {"text":"]","textRange":"21:15::16"}, + {"text":"0","textRange":"21:22::23"}, + {"text":",","textRange":"21:20::21"}, + {"text":")","textRange":"21:23::24"}, + {"text":"u0","textRange":"22:4::6"}, + {"text":":=","textRange":"22:7::9"}, + {"text":"(","textRange":"22:10::11"}, + {"text":"*","textRange":"22:11::12"}, + {"text":"[","textRange":"22:12::13"}, + {"text":"0","textRange":"22:13::14"}, + {"text":"byte","textRange":"22:15::19"}, + {"text":"]","textRange":"22:14::15"}, + {"text":")","textRange":"22:19::20"}, + {"text":"(","textRange":"22:20::21"}, + {"text":"u","textRange":"22:21::22"}, + {"text":")","textRange":"22:22::23"}, + {"text":"}","textRange":"23:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:24:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "3:0:23:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "3:5::16","name":"allLiterals"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "3:19:23:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "4:1::24","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "4:6::24","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "4:6::10","name":"make"}, + {"@type": "Native", "metaData": "4:10::11","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "4:11::23","nativeKind":"Args([]Expr)","children":[ + {"@type": "Native", "metaData": "4:11::17","nativeKind":"[0](ArrayType)","children":[ + {"@type": "Native", "metaData": "4:11::12","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "4:12::13"}, + {"@type": "Identifier", "metaData": "4:13::17","name":"byte"} + ]}, + {"@type": "Native", "metaData": "4:17::18"}, + {"@type": "IntegerLiteral", "metaData": "4:19::20","value":"2"}, + {"@type": "Native", "metaData": "4:20::21"}, + {"@type": "IntegerLiteral", "metaData": "4:22::23","value":"4"} + ]}, + {"@type": "Native", "metaData": "4:23::24","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "4:1::2","name":"s"}}, + {"@type": "VariableDeclaration", "metaData": "6:4::20","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "6:10::20","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Native", "metaData": "6:10::17","nativeKind":"Fun(ArrayType)","children":[ + {"@type": "Native", "metaData": "6:10::11","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "6:11::12","value":"0"}, + {"@type": "Native", "metaData": "6:12::13"}, + {"@type": "Identifier", "metaData": "6:13::17","name":"byte"} + ]}, + {"@type": "Native", "metaData": "6:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "6:18::19","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "6:18::19","name":"s"} + ]}, + {"@type": "Native", "metaData": "6:19::20","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "6:4::6","name":"a0"}}, + {"@type": "VariableDeclaration", "metaData": "7:4::24","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "7:10::24","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Native", "metaData": "7:10::17","nativeKind":"Fun(ArrayType)","children":[ + {"@type": "Native", "metaData": "7:10::11","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "7:11::12","value":"1"}, + {"@type": "Native", "metaData": "7:12::13"}, + {"@type": "Identifier", "metaData": "7:13::17","name":"byte"} + ]}, + {"@type": "Native", "metaData": "7:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "7:18::23","nativeKind":"Args([]Expr)","children":[ + {"@type": "Native", "metaData": "7:18::23","nativeKind":"[0](SliceExpr)","children":[ + {"@type": "Identifier", "metaData": "7:18::19","name":"s"}, + {"@type": "Native", "metaData": "7:19::20","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "7:20::21","value":"1"}, + {"@type": "Native", "metaData": "7:21::22"}, + {"@type": "Native", "metaData": "7:22::23","nativeKind":"Rbrack"} + ]} + ]}, + {"@type": "Native", "metaData": "7:23::24","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "7:4::6","name":"a1"}}, + {"@type": "VariableDeclaration", "metaData": "8:4::20","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "8:10::20","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Native", "metaData": "8:10::17","nativeKind":"Fun(ArrayType)","children":[ + {"@type": "Native", "metaData": "8:10::11","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "8:11::12","value":"2"}, + {"@type": "Native", "metaData": "8:12::13"}, + {"@type": "Identifier", "metaData": "8:13::17","name":"byte"} + ]}, + {"@type": "Native", "metaData": "8:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "8:18::19","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "8:18::19","name":"s"} + ]}, + {"@type": "Native", "metaData": "8:19::20","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "8:4::6","name":"a2"}}, + {"@type": "VariableDeclaration", "metaData": "9:4::20","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "9:10::20","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Native", "metaData": "9:10::17","nativeKind":"Fun(ArrayType)","children":[ + {"@type": "Native", "metaData": "9:10::11","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "9:11::12","value":"4"}, + {"@type": "Native", "metaData": "9:12::13"}, + {"@type": "Identifier", "metaData": "9:13::17","name":"byte"} + ]}, + {"@type": "Native", "metaData": "9:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "9:18::19","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "9:18::19","name":"s"} + ]}, + {"@type": "Native", "metaData": "9:19::20","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "9:4::6","name":"a4"}}, + {"@type": "VariableDeclaration", "metaData": "11:4::23","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "11:10::23","nativeKind":"[0](CallExpr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "11:10::20","rightParenthesis":"11:19::20","leftParenthesis":"11:10::11","expression": + {"@type": "Native", "metaData": "11:11::19","nativeKind":"X(StarExpr)","children":[ + {"@type": "Native", "metaData": "11:11::12","nativeKind":"Star"}, + {"@type": "Native", "metaData": "11:12::19","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "11:12::13","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "11:13::14","value":"0"}, + {"@type": "Native", "metaData": "11:14::15"}, + {"@type": "Identifier", "metaData": "11:15::19","name":"byte"} + ]} + ]}}, + {"@type": "Native", "metaData": "11:20::21","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "11:21::22","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "11:21::22","name":"s"} + ]}, + {"@type": "Native", "metaData": "11:22::23","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "11:4::6","name":"s0"}}, + {"@type": "VariableDeclaration", "metaData": "12:4::27","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "12:10::27","nativeKind":"[0](CallExpr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "12:10::20","rightParenthesis":"12:19::20","leftParenthesis":"12:10::11","expression": + {"@type": "Native", "metaData": "12:11::19","nativeKind":"X(StarExpr)","children":[ + {"@type": "Native", "metaData": "12:11::12","nativeKind":"Star"}, + {"@type": "Native", "metaData": "12:12::19","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "12:12::13","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "12:13::14","value":"1"}, + {"@type": "Native", "metaData": "12:14::15"}, + {"@type": "Identifier", "metaData": "12:15::19","name":"byte"} + ]} + ]}}, + {"@type": "Native", "metaData": "12:20::21","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "12:21::26","nativeKind":"Args([]Expr)","children":[ + {"@type": "Native", "metaData": "12:21::26","nativeKind":"[0](SliceExpr)","children":[ + {"@type": "Identifier", "metaData": "12:21::22","name":"s"}, + {"@type": "Native", "metaData": "12:22::23","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "12:23::24","value":"1"}, + {"@type": "Native", "metaData": "12:24::25"}, + {"@type": "Native", "metaData": "12:25::26","nativeKind":"Rbrack"} + ]} + ]}, + {"@type": "Native", "metaData": "12:26::27","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "12:4::6","name":"s1"}}, + {"@type": "VariableDeclaration", "metaData": "13:4::23","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "13:10::23","nativeKind":"[0](CallExpr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "13:10::20","rightParenthesis":"13:19::20","leftParenthesis":"13:10::11","expression": + {"@type": "Native", "metaData": "13:11::19","nativeKind":"X(StarExpr)","children":[ + {"@type": "Native", "metaData": "13:11::12","nativeKind":"Star"}, + {"@type": "Native", "metaData": "13:12::19","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "13:12::13","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "13:13::14","value":"2"}, + {"@type": "Native", "metaData": "13:14::15"}, + {"@type": "Identifier", "metaData": "13:15::19","name":"byte"} + ]} + ]}}, + {"@type": "Native", "metaData": "13:20::21","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "13:21::22","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "13:21::22","name":"s"} + ]}, + {"@type": "Native", "metaData": "13:22::23","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "13:4::6","name":"s2"}}, + {"@type": "VariableDeclaration", "metaData": "14:4::23","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "14:10::23","nativeKind":"[0](CallExpr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "14:10::20","rightParenthesis":"14:19::20","leftParenthesis":"14:10::11","expression": + {"@type": "Native", "metaData": "14:11::19","nativeKind":"X(StarExpr)","children":[ + {"@type": "Native", "metaData": "14:11::12","nativeKind":"Star"}, + {"@type": "Native", "metaData": "14:12::19","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "14:12::13","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "14:13::14","value":"4"}, + {"@type": "Native", "metaData": "14:14::15"}, + {"@type": "Identifier", "metaData": "14:15::19","name":"byte"} + ]} + ]}}, + {"@type": "Native", "metaData": "14:20::21","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "14:21::22","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "14:21::22","name":"s"} + ]}, + {"@type": "Native", "metaData": "14:22::23","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "14:4::6","name":"s4"}}, + {"@type": "Native", "metaData": "16:4::18","nativeKind":"[9](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "16:4::18","type": + {"@type": "Native", "metaData": "16:10::18","nativeKind":"Type(ArrayType)","children":[ + {"@type": "Native", "metaData": "16:10::11","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "16:11::12"}, + {"@type": "Identifier", "metaData": "16:12::18","name":"string"} + ]},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "16:8::9","name":"t"}} + ]}, + {"@type": "VariableDeclaration", "metaData": "17:4::22","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "17:10::22","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Native", "metaData": "17:10::19","nativeKind":"Fun(ArrayType)","children":[ + {"@type": "Native", "metaData": "17:10::11","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "17:11::12","value":"0"}, + {"@type": "Native", "metaData": "17:12::13"}, + {"@type": "Identifier", "metaData": "17:13::19","name":"string"} + ]}, + {"@type": "Native", "metaData": "17:19::20","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "17:20::21","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "17:20::21","name":"t"} + ]}, + {"@type": "Native", "metaData": "17:21::22","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "17:4::6","name":"t0"}}, + {"@type": "VariableDeclaration", "metaData": "18:4::25","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "18:10::25","nativeKind":"[0](CallExpr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "18:10::22","rightParenthesis":"18:21::22","leftParenthesis":"18:10::11","expression": + {"@type": "Native", "metaData": "18:11::21","nativeKind":"X(StarExpr)","children":[ + {"@type": "Native", "metaData": "18:11::12","nativeKind":"Star"}, + {"@type": "Native", "metaData": "18:12::21","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "18:12::13","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "18:13::14","value":"0"}, + {"@type": "Native", "metaData": "18:14::15"}, + {"@type": "Identifier", "metaData": "18:15::21","name":"string"} + ]} + ]}}, + {"@type": "Native", "metaData": "18:22::23","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "18:23::24","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "18:23::24","name":"t"} + ]}, + {"@type": "Native", "metaData": "18:24::25","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "18:4::6","name":"t1"}}, + {"@type": "VariableDeclaration", "metaData": "19:4::25","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "19:10::25","nativeKind":"[0](CallExpr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "19:10::22","rightParenthesis":"19:21::22","leftParenthesis":"19:10::11","expression": + {"@type": "Native", "metaData": "19:11::21","nativeKind":"X(StarExpr)","children":[ + {"@type": "Native", "metaData": "19:11::12","nativeKind":"Star"}, + {"@type": "Native", "metaData": "19:12::21","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "19:12::13","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "19:13::14","value":"1"}, + {"@type": "Native", "metaData": "19:14::15"}, + {"@type": "Identifier", "metaData": "19:15::21","name":"string"} + ]} + ]}}, + {"@type": "Native", "metaData": "19:22::23","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "19:23::24","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "19:23::24","name":"t"} + ]}, + {"@type": "Native", "metaData": "19:24::25","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "19:4::6","name":"t2"}}, + {"@type": "VariableDeclaration", "metaData": "21:4::24","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "21:9::24","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "21:9::13","name":"make"}, + {"@type": "Native", "metaData": "21:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "21:14::23","nativeKind":"Args([]Expr)","children":[ + {"@type": "Native", "metaData": "21:14::20","nativeKind":"[0](ArrayType)","children":[ + {"@type": "Native", "metaData": "21:14::15","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "21:15::16"}, + {"@type": "Identifier", "metaData": "21:16::20","name":"byte"} + ]}, + {"@type": "Native", "metaData": "21:20::21"}, + {"@type": "IntegerLiteral", "metaData": "21:22::23","value":"0"} + ]}, + {"@type": "Native", "metaData": "21:23::24","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "21:4::5","name":"u"}}, + {"@type": "VariableDeclaration", "metaData": "22:4::23","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "22:10::23","nativeKind":"[0](CallExpr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "22:10::20","rightParenthesis":"22:19::20","leftParenthesis":"22:10::11","expression": + {"@type": "Native", "metaData": "22:11::19","nativeKind":"X(StarExpr)","children":[ + {"@type": "Native", "metaData": "22:11::12","nativeKind":"Star"}, + {"@type": "Native", "metaData": "22:12::19","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "22:12::13","nativeKind":"Lbrack"}, + {"@type": "IntegerLiteral", "metaData": "22:13::14","value":"0"}, + {"@type": "Native", "metaData": "22:14::15"}, + {"@type": "Identifier", "metaData": "22:15::19","name":"byte"} + ]} + ]}}, + {"@type": "Native", "metaData": "22:20::21","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "22:21::22","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "22:21::22","name":"u"} + ]}, + {"@type": "Native", "metaData": "22:22::23","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "22:4::6","name":"u0"}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/binary.go.source b/sonar-go-to-slang/resources/ast/binary.go.source new file mode 100644 index 00000000..f48a5a8c --- /dev/null +++ b/sonar-go-to-slang/resources/ast/binary.go.source @@ -0,0 +1,37 @@ +package main + +/* +https://golang.org/ref/spec#binary_op +binary_op = "||" | "&&" | rel_op | add_op | mul_op . +rel_op = "==" | "!=" | "<" | "<=" | ">" | ">=" . +add_op = "+" | "-" | "|" | "^" . +mul_op = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" . +*/ +func foo() { + booleans := []bool{ + true && false, + false || true, + true && false || false && true, + + 0 == 1, + 0 != 1, + 0 > 1, + 0 >= 1, + 0 < 1, + 0 <= 1, + } + + integers := []int{ + 0 + 1, + 0 - 1, + 0 ^ 1, + 0 | 1, + 0 / 1, + 0 * 1, + 0 & 1, + 0 &^ 1, + 0 << 1, + 0 >> 1, + } + +} diff --git a/sonar-go-to-slang/resources/ast/binary.json b/sonar-go-to-slang/resources/ast/binary.json new file mode 100644 index 00000000..160ab44f --- /dev/null +++ b/sonar-go-to-slang/resources/ast/binary.json @@ -0,0 +1,241 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"/*\nhttps://golang.org/ref/spec#binary_op\nbinary_op = \"||\" | \"\u0026\u0026\" | rel_op | add_op | mul_op .\nrel_op = \"==\" | \"!=\" | \"\u003c\" | \"\u003c=\" | \"\u003e\" | \"\u003e=\" .\nadd_op = \"+\" | \"-\" | \"|\" | \"^\" .\nmul_op = \"*\" | \"/\" | \"%\" | \"\u003c\u003c\" | \"\u003e\u003e\" | \"\u0026\" | \"\u0026^\" .\n*/", "contentText":"\nhttps://golang.org/ref/spec#binary_op\nbinary_op = \"||\" | \"\u0026\u0026\" | rel_op | add_op | mul_op .\nrel_op = \"==\" | \"!=\" | \"\u003c\" | \"\u003c=\" | \"\u003e\" | \"\u003e=\" .\nadd_op = \"+\" | \"-\" | \"|\" | \"^\" .\nmul_op = \"*\" | \"/\" | \"%\" | \"\u003c\u003c\" | \"\u003e\u003e\" | \"\u0026\" | \"\u0026^\" .\n", "range":"3:0:9:2", "contentRange": "3:2:9:0"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"func","textRange":"10:0::4","type":"KEYWORD"}, + {"text":"foo","textRange":"10:5::8"}, + {"text":"(","textRange":"10:8::9"}, + {"text":")","textRange":"10:9::10"}, + {"text":"{","textRange":"10:11::12"}, + {"text":"booleans","textRange":"11:1::9"}, + {"text":":=","textRange":"11:10::12"}, + {"text":"[","textRange":"11:13::14"}, + {"text":"bool","textRange":"11:15::19"}, + {"text":"]","textRange":"11:14::15"}, + {"text":"{","textRange":"11:19::20"}, + {"text":"true","textRange":"12:2::6"}, + {"text":"\u0026\u0026","textRange":"12:7::9"}, + {"text":"false","textRange":"12:10::15"}, + {"text":"false","textRange":"13:2::7"}, + {"text":"||","textRange":"13:8::10"}, + {"text":"true","textRange":"13:11::15"}, + {"text":",","textRange":"12:15::16"}, + {"text":"true","textRange":"14:2::6"}, + {"text":"\u0026\u0026","textRange":"14:7::9"}, + {"text":"false","textRange":"14:10::15"}, + {"text":"||","textRange":"14:16::18"}, + {"text":"false","textRange":"14:19::24"}, + {"text":"\u0026\u0026","textRange":"14:25::27"}, + {"text":"true","textRange":"14:28::32"}, + {"text":",","textRange":"13:15::16"}, + {"text":"0","textRange":"16:2::3"}, + {"text":"==","textRange":"16:4::6"}, + {"text":"1","textRange":"16:7::8"}, + {"text":",","textRange":"14:32::33"}, + {"text":"0","textRange":"17:2::3"}, + {"text":"!=","textRange":"17:4::6"}, + {"text":"1","textRange":"17:7::8"}, + {"text":",","textRange":"16:8::9"}, + {"text":"0","textRange":"18:2::3"}, + {"text":"\u003e","textRange":"18:4::5"}, + {"text":"1","textRange":"18:6::7"}, + {"text":",","textRange":"17:8::9"}, + {"text":"0","textRange":"19:2::3"}, + {"text":"\u003e=","textRange":"19:4::6"}, + {"text":"1","textRange":"19:7::8"}, + {"text":",","textRange":"18:7::8"}, + {"text":"0","textRange":"20:2::3"}, + {"text":"\u003c","textRange":"20:4::5"}, + {"text":"1","textRange":"20:6::7"}, + {"text":",","textRange":"19:8::9"}, + {"text":"0","textRange":"21:2::3"}, + {"text":"\u003c=","textRange":"21:4::6"}, + {"text":"1","textRange":"21:7::8"}, + {"text":",","textRange":"20:7::8"}, + {"text":"}","textRange":"22:1::2"}, + {"text":",","textRange":"21:8::9"}, + {"text":"integers","textRange":"24:1::9"}, + {"text":":=","textRange":"24:10::12"}, + {"text":"[","textRange":"24:13::14"}, + {"text":"int","textRange":"24:15::18"}, + {"text":"]","textRange":"24:14::15"}, + {"text":"{","textRange":"24:18::19"}, + {"text":"0","textRange":"25:2::3"}, + {"text":"+","textRange":"25:4::5"}, + {"text":"1","textRange":"25:6::7"}, + {"text":"0","textRange":"26:2::3"}, + {"text":"-","textRange":"26:4::5"}, + {"text":"1","textRange":"26:6::7"}, + {"text":",","textRange":"25:7::8"}, + {"text":"0","textRange":"27:2::3"}, + {"text":"^","textRange":"27:4::5"}, + {"text":"1","textRange":"27:6::7"}, + {"text":",","textRange":"26:7::8"}, + {"text":"0","textRange":"28:2::3"}, + {"text":"|","textRange":"28:4::5"}, + {"text":"1","textRange":"28:6::7"}, + {"text":",","textRange":"27:7::8"}, + {"text":"0","textRange":"29:2::3"}, + {"text":"/","textRange":"29:4::5"}, + {"text":"1","textRange":"29:6::7"}, + {"text":",","textRange":"28:7::8"}, + {"text":"0","textRange":"30:2::3"}, + {"text":"*","textRange":"30:4::5"}, + {"text":"1","textRange":"30:6::7"}, + {"text":",","textRange":"29:7::8"}, + {"text":"0","textRange":"31:2::3"}, + {"text":"\u0026","textRange":"31:4::5"}, + {"text":"1","textRange":"31:6::7"}, + {"text":",","textRange":"30:7::8"}, + {"text":"0","textRange":"32:2::3"}, + {"text":"\u0026^","textRange":"32:4::6"}, + {"text":"1","textRange":"32:7::8"}, + {"text":",","textRange":"31:7::8"}, + {"text":"0","textRange":"33:2::3"}, + {"text":"\u003c\u003c","textRange":"33:4::6"}, + {"text":"1","textRange":"33:7::8"}, + {"text":",","textRange":"32:8::9"}, + {"text":"0","textRange":"34:2::3"}, + {"text":"\u003e\u003e","textRange":"34:4::6"}, + {"text":"1","textRange":"34:7::8"}, + {"text":",","textRange":"33:8::9"}, + {"text":"}","textRange":"35:1::2"}, + {"text":",","textRange":"34:8::9"}, + {"text":"}","textRange":"37:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:38:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "10:0:37:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "10:5::8","name":"foo"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "10:11:37:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "11:1:22:2","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "11:13:22:2","nativeKind":"[0](CompositeLit)","children":[ + {"@type": "Native", "metaData": "11:13::19","nativeKind":"Type(ArrayType)","children":[ + {"@type": "Native", "metaData": "11:13::14","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "11:14::15"}, + {"@type": "Identifier", "metaData": "11:15::19","name":"bool"} + ]}, + {"@type": "Native", "metaData": "11:19::20","nativeKind":"Lbrace"}, + {"@type": "Native", "metaData": "12:2:21:8","nativeKind":"Elts([]Expr)","children":[ + {"@type": "BinaryExpression", "metaData": "12:2::15","rightOperand": + {"@type": "Literal", "metaData": "12:10::15","value":"false"},"operatorToken":"12:7::9","operator":"CONDITIONAL_AND","leftOperand": + {"@type": "Literal", "metaData": "12:2::6","value":"true"}}, + {"@type": "Native", "metaData": "12:15::16"}, + {"@type": "BinaryExpression", "metaData": "13:2::15","rightOperand": + {"@type": "Literal", "metaData": "13:11::15","value":"true"},"operatorToken":"13:8::10","operator":"CONDITIONAL_OR","leftOperand": + {"@type": "Literal", "metaData": "13:2::7","value":"false"}}, + {"@type": "Native", "metaData": "13:15::16"}, + {"@type": "BinaryExpression", "metaData": "14:2::32","rightOperand": + {"@type": "BinaryExpression", "metaData": "14:19::32","rightOperand": + {"@type": "Literal", "metaData": "14:28::32","value":"true"},"operatorToken":"14:25::27","operator":"CONDITIONAL_AND","leftOperand": + {"@type": "Literal", "metaData": "14:19::24","value":"false"}},"operatorToken":"14:16::18","operator":"CONDITIONAL_OR","leftOperand": + {"@type": "BinaryExpression", "metaData": "14:2::15","rightOperand": + {"@type": "Literal", "metaData": "14:10::15","value":"false"},"operatorToken":"14:7::9","operator":"CONDITIONAL_AND","leftOperand": + {"@type": "Literal", "metaData": "14:2::6","value":"true"}}}, + {"@type": "Native", "metaData": "14:32::33"}, + {"@type": "BinaryExpression", "metaData": "16:2::8","rightOperand": + {"@type": "IntegerLiteral", "metaData": "16:7::8","value":"1"},"operatorToken":"16:4::6","operator":"EQUAL_TO","leftOperand": + {"@type": "IntegerLiteral", "metaData": "16:2::3","value":"0"}}, + {"@type": "Native", "metaData": "16:8::9"}, + {"@type": "BinaryExpression", "metaData": "17:2::8","rightOperand": + {"@type": "IntegerLiteral", "metaData": "17:7::8","value":"1"},"operatorToken":"17:4::6","operator":"NOT_EQUAL_TO","leftOperand": + {"@type": "IntegerLiteral", "metaData": "17:2::3","value":"0"}}, + {"@type": "Native", "metaData": "17:8::9"}, + {"@type": "BinaryExpression", "metaData": "18:2::7","rightOperand": + {"@type": "IntegerLiteral", "metaData": "18:6::7","value":"1"},"operatorToken":"18:4::5","operator":"GREATER_THAN","leftOperand": + {"@type": "IntegerLiteral", "metaData": "18:2::3","value":"0"}}, + {"@type": "Native", "metaData": "18:7::8"}, + {"@type": "BinaryExpression", "metaData": "19:2::8","rightOperand": + {"@type": "IntegerLiteral", "metaData": "19:7::8","value":"1"},"operatorToken":"19:4::6","operator":"GREATER_THAN_OR_EQUAL_TO","leftOperand": + {"@type": "IntegerLiteral", "metaData": "19:2::3","value":"0"}}, + {"@type": "Native", "metaData": "19:8::9"}, + {"@type": "BinaryExpression", "metaData": "20:2::7","rightOperand": + {"@type": "IntegerLiteral", "metaData": "20:6::7","value":"1"},"operatorToken":"20:4::5","operator":"LESS_THAN","leftOperand": + {"@type": "IntegerLiteral", "metaData": "20:2::3","value":"0"}}, + {"@type": "Native", "metaData": "20:7::8"}, + {"@type": "BinaryExpression", "metaData": "21:2::8","rightOperand": + {"@type": "IntegerLiteral", "metaData": "21:7::8","value":"1"},"operatorToken":"21:4::6","operator":"LESS_THAN_OR_EQUAL_TO","leftOperand": + {"@type": "IntegerLiteral", "metaData": "21:2::3","value":"0"}} + ]}, + {"@type": "Native", "metaData": "21:8::9"}, + {"@type": "Native", "metaData": "22:1::2","nativeKind":"Rbrace"} + ]},"identifier": + {"@type": "Identifier", "metaData": "11:1::9","name":"booleans"}}, + {"@type": "VariableDeclaration", "metaData": "24:1:35:2","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "24:13:35:2","nativeKind":"[0](CompositeLit)","children":[ + {"@type": "Native", "metaData": "24:13::18","nativeKind":"Type(ArrayType)","children":[ + {"@type": "Native", "metaData": "24:13::14","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "24:14::15"}, + {"@type": "Identifier", "metaData": "24:15::18","name":"int"} + ]}, + {"@type": "Native", "metaData": "24:18::19","nativeKind":"Lbrace"}, + {"@type": "Native", "metaData": "25:2:34:8","nativeKind":"Elts([]Expr)","children":[ + {"@type": "BinaryExpression", "metaData": "25:2::7","rightOperand": + {"@type": "IntegerLiteral", "metaData": "25:6::7","value":"1"},"operatorToken":"25:4::5","operator":"PLUS","leftOperand": + {"@type": "IntegerLiteral", "metaData": "25:2::3","value":"0"}}, + {"@type": "Native", "metaData": "25:7::8"}, + {"@type": "BinaryExpression", "metaData": "26:2::7","rightOperand": + {"@type": "IntegerLiteral", "metaData": "26:6::7","value":"1"},"operatorToken":"26:4::5","operator":"MINUS","leftOperand": + {"@type": "IntegerLiteral", "metaData": "26:2::3","value":"0"}}, + {"@type": "Native", "metaData": "26:7::8"}, + {"@type": "Native", "metaData": "27:2::7","nativeKind":"[2](BinaryExpr)","children":[ + {"@type": "IntegerLiteral", "metaData": "27:2::3","value":"0"}, + {"@type": "Native", "metaData": "27:4::5","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "27:6::7","value":"1"} + ]}, + {"@type": "Native", "metaData": "27:7::8"}, + {"@type": "Native", "metaData": "28:2::7","nativeKind":"[3](BinaryExpr)","children":[ + {"@type": "IntegerLiteral", "metaData": "28:2::3","value":"0"}, + {"@type": "Native", "metaData": "28:4::5","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "28:6::7","value":"1"} + ]}, + {"@type": "Native", "metaData": "28:7::8"}, + {"@type": "BinaryExpression", "metaData": "29:2::7","rightOperand": + {"@type": "IntegerLiteral", "metaData": "29:6::7","value":"1"},"operatorToken":"29:4::5","operator":"DIVIDED_BY","leftOperand": + {"@type": "IntegerLiteral", "metaData": "29:2::3","value":"0"}}, + {"@type": "Native", "metaData": "29:7::8"}, + {"@type": "BinaryExpression", "metaData": "30:2::7","rightOperand": + {"@type": "IntegerLiteral", "metaData": "30:6::7","value":"1"},"operatorToken":"30:4::5","operator":"TIMES","leftOperand": + {"@type": "IntegerLiteral", "metaData": "30:2::3","value":"0"}}, + {"@type": "Native", "metaData": "30:7::8"}, + {"@type": "Native", "metaData": "31:2::7","nativeKind":"[6](BinaryExpr)","children":[ + {"@type": "IntegerLiteral", "metaData": "31:2::3","value":"0"}, + {"@type": "Native", "metaData": "31:4::5","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "31:6::7","value":"1"} + ]}, + {"@type": "Native", "metaData": "31:7::8"}, + {"@type": "Native", "metaData": "32:2::8","nativeKind":"[7](BinaryExpr)","children":[ + {"@type": "IntegerLiteral", "metaData": "32:2::3","value":"0"}, + {"@type": "Native", "metaData": "32:4::6","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "32:7::8","value":"1"} + ]}, + {"@type": "Native", "metaData": "32:8::9"}, + {"@type": "Native", "metaData": "33:2::8","nativeKind":"[8](BinaryExpr)","children":[ + {"@type": "IntegerLiteral", "metaData": "33:2::3","value":"0"}, + {"@type": "Native", "metaData": "33:4::6","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "33:7::8","value":"1"} + ]}, + {"@type": "Native", "metaData": "33:8::9"}, + {"@type": "Native", "metaData": "34:2::8","nativeKind":"[9](BinaryExpr)","children":[ + {"@type": "IntegerLiteral", "metaData": "34:2::3","value":"0"}, + {"@type": "Native", "metaData": "34:4::6","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "34:7::8","value":"1"} + ]} + ]}, + {"@type": "Native", "metaData": "34:8::9"}, + {"@type": "Native", "metaData": "35:1::2","nativeKind":"Rbrace"} + ]},"identifier": + {"@type": "Identifier", "metaData": "24:1::9","name":"integers"}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/class.go.source b/sonar-go-to-slang/resources/ast/class.go.source new file mode 100644 index 00000000..6621b81c --- /dev/null +++ b/sonar-go-to-slang/resources/ast/class.go.source @@ -0,0 +1,35 @@ +package ast +import "fmt" + + +type class1 struct { x, y int } +type Class2 struct { a, b string } + +type class3 struct { + Value string + TextRange *TextRange + TokenType string +} + +type class4 struct {} + +var anonymous Class2 = struct { Class2 }{} //Not a TypeSpec, but a StructType, it will not be a class + +type class5 interface { + f1() float64 + f2() float64 +} + +type Class6 interface {} + +func foo(notClass3 interface{ bar() }) { //Not a TypeSpec, but a InterfaceType; not a class + var anonymous Class2 = struct { Class2 }{} +} + +func bar(x int) { + type class7 struct { x, y int } +} + +type ( + RrsType string +) diff --git a/sonar-go-to-slang/resources/ast/class.json b/sonar-go-to-slang/resources/ast/class.json new file mode 100644 index 00000000..dba4453c --- /dev/null +++ b/sonar-go-to-slang/resources/ast/class.json @@ -0,0 +1,380 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"//Not a TypeSpec, but a StructType, it will not be a class", "contentText":"Not a TypeSpec, but a StructType, it will not be a class", "range":"16:43::101", "contentRange": "16:45::101"}, + {"text":"//Not a TypeSpec, but a InterfaceType; not a class", "contentText":"Not a TypeSpec, but a InterfaceType; not a class", "range":"25:41::91", "contentRange": "25:43::91"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"}, + {"text":"import","textRange":"2:0::6","type":"KEYWORD"}, + {"text":"\"fmt\"","textRange":"2:7::12","type":"STRING_LITERAL"}, + {"text":"type","textRange":"5:0::4","type":"KEYWORD"}, + {"text":"class1","textRange":"5:5::11"}, + {"text":"struct","textRange":"5:12::18","type":"KEYWORD"}, + {"text":"{","textRange":"5:19::20"}, + {"text":"x","textRange":"5:21::22"}, + {"text":"y","textRange":"5:24::25"}, + {"text":",","textRange":"5:22::23"}, + {"text":"int","textRange":"5:26::29"}, + {"text":"}","textRange":"5:30::31"}, + {"text":"type","textRange":"6:0::4","type":"KEYWORD"}, + {"text":"Class2","textRange":"6:5::11"}, + {"text":"struct","textRange":"6:12::18","type":"KEYWORD"}, + {"text":"{","textRange":"6:19::20"}, + {"text":"a","textRange":"6:21::22"}, + {"text":"b","textRange":"6:24::25"}, + {"text":",","textRange":"6:22::23"}, + {"text":"string","textRange":"6:26::32"}, + {"text":"}","textRange":"6:33::34"}, + {"text":"type","textRange":"8:0::4","type":"KEYWORD"}, + {"text":"class3","textRange":"8:5::11"}, + {"text":"struct","textRange":"8:12::18","type":"KEYWORD"}, + {"text":"{","textRange":"8:19::20"}, + {"text":"Value","textRange":"9:1::6"}, + {"text":"string","textRange":"9:11::17"}, + {"text":"TextRange","textRange":"10:1::10"}, + {"text":"*","textRange":"10:11::12"}, + {"text":"TextRange","textRange":"10:12::21"}, + {"text":"TokenType","textRange":"11:1::10"}, + {"text":"string","textRange":"11:11::17"}, + {"text":"}","textRange":"12:0::1"}, + {"text":"type","textRange":"14:0::4","type":"KEYWORD"}, + {"text":"class4","textRange":"14:5::11"}, + {"text":"struct","textRange":"14:12::18","type":"KEYWORD"}, + {"text":"{","textRange":"14:19::20"}, + {"text":"}","textRange":"14:20::21"}, + {"text":"var","textRange":"16:0::3","type":"KEYWORD"}, + {"text":"anonymous","textRange":"16:4::13"}, + {"text":"Class2","textRange":"16:14::20"}, + {"text":"struct","textRange":"16:23::29","type":"KEYWORD"}, + {"text":"{","textRange":"16:30::31"}, + {"text":"Class2","textRange":"16:32::38"}, + {"text":"}","textRange":"16:39::40"}, + {"text":"{","textRange":"16:40::41"}, + {"text":"}","textRange":"16:41::42"}, + {"text":"=","textRange":"16:21::22"}, + {"text":"type","textRange":"18:0::4","type":"KEYWORD"}, + {"text":"class5","textRange":"18:5::11"}, + {"text":"interface","textRange":"18:12::21","type":"KEYWORD"}, + {"text":"{","textRange":"18:22::23"}, + {"text":"f1","textRange":"19:4::6"}, + {"text":"(","textRange":"19:6::7"}, + {"text":")","textRange":"19:7::8"}, + {"text":"float64","textRange":"19:9::16"}, + {"text":"f2","textRange":"20:4::6"}, + {"text":"(","textRange":"20:6::7"}, + {"text":")","textRange":"20:7::8"}, + {"text":"float64","textRange":"20:9::16"}, + {"text":"}","textRange":"21:0::1"}, + {"text":"type","textRange":"23:0::4","type":"KEYWORD"}, + {"text":"Class6","textRange":"23:5::11"}, + {"text":"interface","textRange":"23:12::21","type":"KEYWORD"}, + {"text":"{","textRange":"23:22::23"}, + {"text":"}","textRange":"23:23::24"}, + {"text":"func","textRange":"25:0::4","type":"KEYWORD"}, + {"text":"foo","textRange":"25:5::8"}, + {"text":"(","textRange":"25:8::9"}, + {"text":"notClass3","textRange":"25:9::18"}, + {"text":"interface","textRange":"25:19::28","type":"KEYWORD"}, + {"text":"{","textRange":"25:28::29"}, + {"text":"bar","textRange":"25:30::33"}, + {"text":"(","textRange":"25:33::34"}, + {"text":")","textRange":"25:34::35"}, + {"text":"}","textRange":"25:36::37"}, + {"text":")","textRange":"25:37::38"}, + {"text":"{","textRange":"25:39::40"}, + {"text":"var","textRange":"26:5::8","type":"KEYWORD"}, + {"text":"anonymous","textRange":"26:9::18"}, + {"text":"Class2","textRange":"26:19::25"}, + {"text":"struct","textRange":"26:28::34","type":"KEYWORD"}, + {"text":"{","textRange":"26:35::36"}, + {"text":"Class2","textRange":"26:37::43"}, + {"text":"}","textRange":"26:44::45"}, + {"text":"{","textRange":"26:45::46"}, + {"text":"}","textRange":"26:46::47"}, + {"text":"=","textRange":"26:26::27"}, + {"text":"}","textRange":"27:0::1"}, + {"text":"func","textRange":"29:0::4","type":"KEYWORD"}, + {"text":"bar","textRange":"29:5::8"}, + {"text":"(","textRange":"29:8::9"}, + {"text":"x","textRange":"29:9::10"}, + {"text":"int","textRange":"29:11::14"}, + {"text":")","textRange":"29:14::15"}, + {"text":"{","textRange":"29:16::17"}, + {"text":"type","textRange":"30:5::9","type":"KEYWORD"}, + {"text":"class7","textRange":"30:10::16"}, + {"text":"struct","textRange":"30:17::23","type":"KEYWORD"}, + {"text":"{","textRange":"30:24::25"}, + {"text":"x","textRange":"30:26::27"}, + {"text":"y","textRange":"30:29::30"}, + {"text":",","textRange":"30:27::28"}, + {"text":"int","textRange":"30:31::34"}, + {"text":"}","textRange":"30:35::36"}, + {"text":"}","textRange":"31:0::1"}, + {"text":"type","textRange":"33:0::4","type":"KEYWORD"}, + {"text":"(","textRange":"33:5::6"}, + {"text":"RrsType","textRange":"34:1::8"}, + {"text":"string","textRange":"34:9::15"}, + {"text":")","textRange":"35:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:36:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]}, + {"@type": "ImportDeclaration", "metaData": "2:0::12","children":[ + {"@type": "Native", "metaData": "2:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "2:7::12","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "2:7::12","value":"\"fmt\"","content":"fmt"} + ]} + ]}, + {"@type": "ClassDeclaration", "metaData": "5:0::31","identifier":"5:5::11","classTree": + {"@type": "Native", "metaData": "5:0::31","nativeKind":"[1](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "5:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "5:5::11","name":"class1"}, + {"@type": "Native", "metaData": "5:12::31","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "5:12::18","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "5:19::31","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "5:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "5:21::29","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "5:21::25","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "5:21::22","name":"x"}, + {"@type": "Native", "metaData": "5:22::23"}, + {"@type": "Identifier", "metaData": "5:24::25","name":"y"} + ]}, + {"@type": "Identifier", "metaData": "5:26::29","name":"int"} + ]}, + {"@type": "Native", "metaData": "5:30::31","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "ClassDeclaration", "metaData": "6:0::34","identifier":"6:5::11","classTree": + {"@type": "Native", "metaData": "6:0::34","nativeKind":"[2](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "6:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "6:5::11","name":"Class2"}, + {"@type": "Native", "metaData": "6:12::34","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "6:12::18","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "6:19::34","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "6:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "6:21::32","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "6:21::25","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "6:21::22","name":"a"}, + {"@type": "Native", "metaData": "6:22::23"}, + {"@type": "Identifier", "metaData": "6:24::25","name":"b"} + ]}, + {"@type": "Identifier", "metaData": "6:26::32","name":"string"} + ]}, + {"@type": "Native", "metaData": "6:33::34","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "ClassDeclaration", "metaData": "8:0:12:1","identifier":"8:5::11","classTree": + {"@type": "Native", "metaData": "8:0:12:1","nativeKind":"[3](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "8:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "8:5::11","name":"class3"}, + {"@type": "Native", "metaData": "8:12:12:1","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "8:12::18","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "8:19:12:1","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "8:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "9:1::17","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "9:1::6","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "9:1::6","name":"Value"} + ]}, + {"@type": "Identifier", "metaData": "9:11::17","name":"string"} + ]}, + {"@type": "Native", "metaData": "10:1::21","nativeKind":"[1](Field)","children":[ + {"@type": "Native", "metaData": "10:1::10","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "10:1::10","name":"TextRange"} + ]}, + {"@type": "Native", "metaData": "10:11::21","nativeKind":"Type(StarExpr)","children":[ + {"@type": "Native", "metaData": "10:11::12","nativeKind":"Star"}, + {"@type": "Identifier", "metaData": "10:12::21","name":"TextRange"} + ]} + ]}, + {"@type": "Native", "metaData": "11:1::17","nativeKind":"[2](Field)","children":[ + {"@type": "Native", "metaData": "11:1::10","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "11:1::10","name":"TokenType"} + ]}, + {"@type": "Identifier", "metaData": "11:11::17","name":"string"} + ]}, + {"@type": "Native", "metaData": "12:0::1","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "ClassDeclaration", "metaData": "14:0::21","identifier":"14:5::11","classTree": + {"@type": "Native", "metaData": "14:0::21","nativeKind":"[4](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "14:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "14:5::11","name":"class4"}, + {"@type": "Native", "metaData": "14:12::21","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "14:12::18","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "14:19::21","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "14:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "14:20::21","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "VariableDeclaration", "metaData": "16:0::42","type": + {"@type": "Identifier", "metaData": "16:14::20","name":"Class2"},"isVal":false,"initializer": + {"@type": "Native", "metaData": "16:23::42","nativeKind":"[0](CompositeLit)","children":[ + {"@type": "Native", "metaData": "16:23::40","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "16:23::29","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "16:30::40","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "16:30::31","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "16:32::38","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "16:32::38","name":"Class2"} + ]}, + {"@type": "Native", "metaData": "16:39::40","nativeKind":"Closing"} + ]} + ]}, + {"@type": "Native", "metaData": "16:40::41","nativeKind":"Lbrace"}, + {"@type": "Native", "metaData": "16:41::42","nativeKind":"Rbrace"} + ]},"identifier": + {"@type": "Identifier", "metaData": "16:4::13","name":"anonymous"}}, + {"@type": "ClassDeclaration", "metaData": "18:0:21:1","identifier":"18:5::11","classTree": + {"@type": "Native", "metaData": "18:0:21:1","nativeKind":"[6](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "18:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "18:5::11","name":"class5"}, + {"@type": "Native", "metaData": "18:12:21:1","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "18:12::21","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "18:22:21:1","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "18:22::23","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "19:4::16","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "19:4::6","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "19:4::6","name":"f1"} + ]}, + {"@type": "Native", "metaData": "19:6::16","nativeKind":"Type(FuncType)","children":[ + {"@type": "Native", "metaData": "19:6::8","nativeKind":"Params(FieldList)","children":[ + {"@type": "Native", "metaData": "19:6::7","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "19:7::8","nativeKind":"Closing"} + ]}, + {"@type": "Native", "metaData": "19:9::16","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "19:9::16","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "19:9::16","name":"float64"} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "20:4::16","nativeKind":"[1](Field)","children":[ + {"@type": "Native", "metaData": "20:4::6","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "20:4::6","name":"f2"} + ]}, + {"@type": "Native", "metaData": "20:6::16","nativeKind":"Type(FuncType)","children":[ + {"@type": "Native", "metaData": "20:6::8","nativeKind":"Params(FieldList)","children":[ + {"@type": "Native", "metaData": "20:6::7","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "20:7::8","nativeKind":"Closing"} + ]}, + {"@type": "Native", "metaData": "20:9::16","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "20:9::16","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "20:9::16","name":"float64"} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "21:0::1","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "ClassDeclaration", "metaData": "23:0::24","identifier":"23:5::11","classTree": + {"@type": "Native", "metaData": "23:0::24","nativeKind":"[7](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "23:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "23:5::11","name":"Class6"}, + {"@type": "Native", "metaData": "23:12::24","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "23:12::21","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "23:22::24","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "23:22::23","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "23:23::24","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "25:0:27:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "25:5::8","name":"foo"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "25:9::37","type": + {"@type": "Native", "metaData": "25:19::37","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "25:19::28","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "25:28::37","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "25:28::29","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "25:30::35","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "25:30::33","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "25:30::33","name":"bar"} + ]}, + {"@type": "Native", "metaData": "25:33::35","nativeKind":"Type(FuncType)","children":[ + {"@type": "Native", "metaData": "25:33::35","nativeKind":"Params(FieldList)","children":[ + {"@type": "Native", "metaData": "25:33::34","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "25:34::35","nativeKind":"Closing"} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "25:36::37","nativeKind":"Closing"} + ]} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "25:9::18","name":"notClass3"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "25:39:27:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "26:5::47","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "26:5::47","type": + {"@type": "Identifier", "metaData": "26:19::25","name":"Class2"},"isVal":false,"initializer": + {"@type": "Native", "metaData": "26:28::47","nativeKind":"[0](CompositeLit)","children":[ + {"@type": "Native", "metaData": "26:28::45","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "26:28::34","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "26:35::45","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "26:35::36","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "26:37::43","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "26:37::43","name":"Class2"} + ]}, + {"@type": "Native", "metaData": "26:44::45","nativeKind":"Closing"} + ]} + ]}, + {"@type": "Native", "metaData": "26:45::46","nativeKind":"Lbrace"}, + {"@type": "Native", "metaData": "26:46::47","nativeKind":"Rbrace"} + ]},"identifier": + {"@type": "Identifier", "metaData": "26:9::18","name":"anonymous"}} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "29:0:31:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "29:5::8","name":"bar"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "29:9::14","type": + {"@type": "Identifier", "metaData": "29:11::14","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "29:9::10","name":"x"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "29:16:31:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "30:5::36","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "ClassDeclaration", "metaData": "30:5::36","identifier":"30:10::16","classTree": + {"@type": "Native", "metaData": "30:5::36","nativeKind":"Decl(TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "30:5::9","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "30:10::16","name":"class7"}, + {"@type": "Native", "metaData": "30:17::36","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "30:17::23","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "30:24::36","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "30:24::25","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "30:26::34","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "30:26::30","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "30:26::27","name":"x"}, + {"@type": "Native", "metaData": "30:27::28"}, + {"@type": "Identifier", "metaData": "30:29::30","name":"y"} + ]}, + {"@type": "Identifier", "metaData": "30:31::34","name":"int"} + ]}, + {"@type": "Native", "metaData": "30:35::36","nativeKind":"Closing"} + ]} + ]} + ]}} + ]} + ]}}, + {"@type": "Native", "metaData": "33:0:35:1","nativeKind":"[10](GenDecl)","children":[ + {"@type": "Native", "metaData": "33:0::4","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "33:5::6","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "34:1::15","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "34:1::15","nativeKind":"[0](TypeSpec)","children":[ + {"@type": "Identifier", "metaData": "34:1::8","name":"RrsType"}, + {"@type": "Identifier", "metaData": "34:9::15","name":"string"} + ]} + ]}, + {"@type": "Native", "metaData": "35:0::1","nativeKind":"Rparen"} + ]} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/declaration_assignment.go.source b/sonar-go-to-slang/resources/ast/declaration_assignment.go.source new file mode 100644 index 00000000..94c04028 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/declaration_assignment.go.source @@ -0,0 +1,47 @@ +package ast + +import "fmt" + +func main() { + var x int + var y int = 42 + var z = 42 + w := 42 + + var a string + b := "??" + a = "42" + + x = 4 + x += 30 //Only += and = are mapped to assignment, other are mapped to native nodes (see AssignmentExpressionTree interface) + x *= 12 + x /= 4 + x %= 7 + x -= 46 + + y = (32) + + cond1, cond2, string1:= true, false, "no!" + r1, r2 := foo() + var i, j int = 1, 2 + i, j = j, i + i,/*comment0*/ j = j/*comment0*/, i + + const Pi = 3.14 + const g float = 9.81 + + + var /*comment0*/ii/*comment1*/,/*comment2*/jj/*comment3*/int = 11, 22 + var ii, jj int =/*comment4*/11/*comment4*/,/*comment4*/22//Comment2 + + var (abc int) + var ( //Comment01 + aa int + bb string + cc bool + ) +} + +func foo() (int, string) { + return 42, "42" +} \ No newline at end of file diff --git a/sonar-go-to-slang/resources/ast/declaration_assignment.json b/sonar-go-to-slang/resources/ast/declaration_assignment.json new file mode 100644 index 00000000..f296ebb5 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/declaration_assignment.json @@ -0,0 +1,454 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"//Only += and = are mapped to assignment, other are mapped to native nodes (see AssignmentExpressionTree interface)", "contentText":"Only += and = are mapped to assignment, other are mapped to native nodes (see AssignmentExpressionTree interface)", "range":"16:9::124", "contentRange": "16:11::124"}, + {"text":"/*comment0*/", "contentText":"comment0", "range":"28:3::15", "contentRange": "28:5::13"}, + {"text":"/*comment0*/", "contentText":"comment0", "range":"28:21::33", "contentRange": "28:23::31"}, + {"text":"/*comment0*/", "contentText":"comment0", "range":"34:8::20", "contentRange": "34:10::18"}, + {"text":"/*comment1*/", "contentText":"comment1", "range":"34:22::34", "contentRange": "34:24::32"}, + {"text":"/*comment2*/", "contentText":"comment2", "range":"34:35::47", "contentRange": "34:37::45"}, + {"text":"/*comment3*/", "contentText":"comment3", "range":"34:49::61", "contentRange": "34:51::59"}, + {"text":"/*comment4*/", "contentText":"comment4", "range":"35:20::32", "contentRange": "35:22::30"}, + {"text":"/*comment4*/", "contentText":"comment4", "range":"35:34::46", "contentRange": "35:36::44"}, + {"text":"/*comment4*/", "contentText":"comment4", "range":"35:47::59", "contentRange": "35:49::57"}, + {"text":"//Comment2", "contentText":"Comment2", "range":"35:61::71", "contentRange": "35:63::71"}, + {"text":"//Comment01", "contentText":"Comment01", "range":"38:10::21", "contentRange": "38:12::21"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"}, + {"text":"import","textRange":"3:0::6","type":"KEYWORD"}, + {"text":"\"fmt\"","textRange":"3:7::12","type":"STRING_LITERAL"}, + {"text":"func","textRange":"5:0::4","type":"KEYWORD"}, + {"text":"main","textRange":"5:5::9"}, + {"text":"(","textRange":"5:9::10"}, + {"text":")","textRange":"5:10::11"}, + {"text":"{","textRange":"5:12::13"}, + {"text":"var","textRange":"6:1::4","type":"KEYWORD"}, + {"text":"x","textRange":"6:5::6"}, + {"text":"int","textRange":"6:7::10"}, + {"text":"var","textRange":"7:1::4","type":"KEYWORD"}, + {"text":"y","textRange":"7:5::6"}, + {"text":"int","textRange":"7:7::10"}, + {"text":"42","textRange":"7:13::15"}, + {"text":"=","textRange":"7:11::12"}, + {"text":"var","textRange":"8:1::4","type":"KEYWORD"}, + {"text":"z","textRange":"8:5::6"}, + {"text":"42","textRange":"8:9::11"}, + {"text":"=","textRange":"8:7::8"}, + {"text":"w","textRange":"9:1::2"}, + {"text":":=","textRange":"9:3::5"}, + {"text":"42","textRange":"9:6::8"}, + {"text":"var","textRange":"11:1::4","type":"KEYWORD"}, + {"text":"a","textRange":"11:5::6"}, + {"text":"string","textRange":"11:7::13"}, + {"text":"b","textRange":"12:1::2"}, + {"text":":=","textRange":"12:3::5"}, + {"text":"\"??\"","textRange":"12:6::10","type":"STRING_LITERAL"}, + {"text":"a","textRange":"13:1::2"}, + {"text":"=","textRange":"13:3::4"}, + {"text":"\"42\"","textRange":"13:5::9","type":"STRING_LITERAL"}, + {"text":"x","textRange":"15:1::2"}, + {"text":"=","textRange":"15:3::4"}, + {"text":"4","textRange":"15:5::6"}, + {"text":"x","textRange":"16:1::2"}, + {"text":"+=","textRange":"16:3::5"}, + {"text":"30","textRange":"16:6::8"}, + {"text":"x","textRange":"17:1::2"}, + {"text":"*=","textRange":"17:3::5"}, + {"text":"12","textRange":"17:6::8"}, + {"text":"x","textRange":"18:1::2"}, + {"text":"/=","textRange":"18:3::5"}, + {"text":"4","textRange":"18:6::7"}, + {"text":"x","textRange":"19:1::2"}, + {"text":"%=","textRange":"19:3::5"}, + {"text":"7","textRange":"19:6::7"}, + {"text":"x","textRange":"20:1::2"}, + {"text":"-=","textRange":"20:3::5"}, + {"text":"46","textRange":"20:6::8"}, + {"text":"y","textRange":"22:1::2"}, + {"text":"=","textRange":"22:3::4"}, + {"text":"(","textRange":"22:5::6"}, + {"text":"32","textRange":"22:6::8"}, + {"text":")","textRange":"22:8::9"}, + {"text":"cond1","textRange":"24:1::6"}, + {"text":"cond2","textRange":"24:8::13"}, + {"text":",","textRange":"24:6::7"}, + {"text":"string1","textRange":"24:15::22"}, + {"text":",","textRange":"24:13::14"}, + {"text":":=","textRange":"24:22::24"}, + {"text":"true","textRange":"24:25::29"}, + {"text":"false","textRange":"24:31::36"}, + {"text":",","textRange":"24:29::30"}, + {"text":"\"no!\"","textRange":"24:38::43","type":"STRING_LITERAL"}, + {"text":",","textRange":"24:36::37"}, + {"text":"r1","textRange":"25:1::3"}, + {"text":"r2","textRange":"25:5::7"}, + {"text":",","textRange":"25:3::4"}, + {"text":":=","textRange":"25:8::10"}, + {"text":"foo","textRange":"25:11::14"}, + {"text":"(","textRange":"25:14::15"}, + {"text":")","textRange":"25:15::16"}, + {"text":"var","textRange":"26:1::4","type":"KEYWORD"}, + {"text":"i","textRange":"26:5::6"}, + {"text":"j","textRange":"26:8::9"}, + {"text":",","textRange":"26:6::7"}, + {"text":"int","textRange":"26:10::13"}, + {"text":"1","textRange":"26:16::17"}, + {"text":"2","textRange":"26:19::20"}, + {"text":",","textRange":"26:17::18"}, + {"text":"=","textRange":"26:14::15"}, + {"text":"i","textRange":"27:1::2"}, + {"text":"j","textRange":"27:4::5"}, + {"text":",","textRange":"27:2::3"}, + {"text":"=","textRange":"27:6::7"}, + {"text":"j","textRange":"27:8::9"}, + {"text":"i","textRange":"27:11::12"}, + {"text":",","textRange":"27:9::10"}, + {"text":"i","textRange":"28:1::2"}, + {"text":"j","textRange":"28:16::17"}, + {"text":",","textRange":"28:2::3"}, + {"text":"=","textRange":"28:18::19"}, + {"text":"j","textRange":"28:20::21"}, + {"text":"i","textRange":"28:35::36"}, + {"text":",","textRange":"28:33::34"}, + {"text":"const","textRange":"30:1::6","type":"KEYWORD"}, + {"text":"Pi","textRange":"30:7::9"}, + {"text":"3.14","textRange":"30:12::16"}, + {"text":"=","textRange":"30:10::11"}, + {"text":"const","textRange":"31:1::6","type":"KEYWORD"}, + {"text":"g","textRange":"31:7::8"}, + {"text":"float","textRange":"31:9::14"}, + {"text":"9.81","textRange":"31:17::21"}, + {"text":"=","textRange":"31:15::16"}, + {"text":"var","textRange":"34:4::7","type":"KEYWORD"}, + {"text":"ii","textRange":"34:20::22"}, + {"text":"jj","textRange":"34:47::49"}, + {"text":",","textRange":"34:34::35"}, + {"text":"int","textRange":"34:61::64"}, + {"text":"11","textRange":"34:67::69"}, + {"text":"22","textRange":"34:71::73"}, + {"text":",","textRange":"34:69::70"}, + {"text":"=","textRange":"34:65::66"}, + {"text":"var","textRange":"35:4::7","type":"KEYWORD"}, + {"text":"ii","textRange":"35:8::10"}, + {"text":"jj","textRange":"35:12::14"}, + {"text":",","textRange":"35:10::11"}, + {"text":"int","textRange":"35:15::18"}, + {"text":"11","textRange":"35:32::34"}, + {"text":"22","textRange":"35:59::61"}, + {"text":",","textRange":"35:46::47"}, + {"text":"=","textRange":"35:19::20"}, + {"text":"var","textRange":"37:4::7","type":"KEYWORD"}, + {"text":"(","textRange":"37:8::9"}, + {"text":"abc","textRange":"37:9::12"}, + {"text":"int","textRange":"37:13::16"}, + {"text":")","textRange":"37:16::17"}, + {"text":"var","textRange":"38:4::7","type":"KEYWORD"}, + {"text":"(","textRange":"38:8::9"}, + {"text":"aa","textRange":"39:8::10"}, + {"text":"int","textRange":"39:11::14"}, + {"text":"bb","textRange":"40:8::10"}, + {"text":"string","textRange":"40:11::17"}, + {"text":"cc","textRange":"41:8::10"}, + {"text":"bool","textRange":"41:11::15"}, + {"text":")","textRange":"42:4::5"}, + {"text":"}","textRange":"43:0::1"}, + {"text":"func","textRange":"45:0::4","type":"KEYWORD"}, + {"text":"foo","textRange":"45:5::8"}, + {"text":"(","textRange":"45:8::9"}, + {"text":")","textRange":"45:9::10"}, + {"text":"(","textRange":"45:11::12"}, + {"text":"int","textRange":"45:12::15"}, + {"text":"string","textRange":"45:17::23"}, + {"text":",","textRange":"45:15::16"}, + {"text":")","textRange":"45:23::24"}, + {"text":"{","textRange":"45:25::26"}, + {"text":"return","textRange":"46:1::7","type":"KEYWORD"}, + {"text":"42","textRange":"46:8::10"}, + {"text":"\"42\"","textRange":"46:12::16","type":"STRING_LITERAL"}, + {"text":",","textRange":"46:10::11"}, + {"text":"}","textRange":"47:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:47:1","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]}, + {"@type": "ImportDeclaration", "metaData": "3:0::12","children":[ + {"@type": "Native", "metaData": "3:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "3:7::12","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "3:7::12","value":"\"fmt\"","content":"fmt"} + ]} + ]}, + {"@type": "FunctionDeclaration", "metaData": "5:0:43:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "5:5::9","name":"main"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "5:12:43:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "6:1::10","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "6:1::10","type": + {"@type": "Identifier", "metaData": "6:7::10","name":"int"},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "6:5::6","name":"x"}} + ]}, + {"@type": "Native", "metaData": "7:1::15","nativeKind":"[1](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "7:1::15","type": + {"@type": "Identifier", "metaData": "7:7::10","name":"int"},"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "7:13::15","value":"42"},"identifier": + {"@type": "Identifier", "metaData": "7:5::6","name":"y"}} + ]}, + {"@type": "Native", "metaData": "8:1::11","nativeKind":"[2](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "8:1::11","type": + null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "8:9::11","value":"42"},"identifier": + {"@type": "Identifier", "metaData": "8:5::6","name":"z"}} + ]}, + {"@type": "VariableDeclaration", "metaData": "9:1::8","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "9:6::8","value":"42"},"identifier": + {"@type": "Identifier", "metaData": "9:1::2","name":"w"}}, + {"@type": "Native", "metaData": "11:1::13","nativeKind":"[4](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "11:1::13","type": + {"@type": "Identifier", "metaData": "11:7::13","name":"string"},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "11:5::6","name":"a"}} + ]}, + {"@type": "VariableDeclaration", "metaData": "12:1::10","type":null,"isVal":false,"initializer": + {"@type": "StringLiteral", "metaData": "12:6::10","value":"\"??\"","content":"??"},"identifier": + {"@type": "Identifier", "metaData": "12:1::2","name":"b"}}, + {"@type": "AssignmentExpression", "metaData": "13:1::9","statementOrExpression": + {"@type": "StringLiteral", "metaData": "13:5::9","value":"\"42\"","content":"42"},"operator":"EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "13:1::2","name":"a"}}, + {"@type": "AssignmentExpression", "metaData": "15:1::6","statementOrExpression": + {"@type": "IntegerLiteral", "metaData": "15:5::6","value":"4"},"operator":"EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "15:1::2","name":"x"}}, + {"@type": "AssignmentExpression", "metaData": "16:1::8","statementOrExpression": + {"@type": "IntegerLiteral", "metaData": "16:6::8","value":"30"},"operator":"PLUS_EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "16:1::2","name":"x"}}, + {"@type": "Native", "metaData": "17:1::8","nativeKind":"[9](AssignStmt)","children":[ + {"@type": "Native", "metaData": "17:1::2","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "17:1::2","name":"x"} + ]}, + {"@type": "Native", "metaData": "17:3::5","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "17:6::8","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "17:6::8","value":"12"} + ]} + ]}, + {"@type": "Native", "metaData": "18:1::7","nativeKind":"[10](AssignStmt)","children":[ + {"@type": "Native", "metaData": "18:1::2","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "18:1::2","name":"x"} + ]}, + {"@type": "Native", "metaData": "18:3::5","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "18:6::7","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "18:6::7","value":"4"} + ]} + ]}, + {"@type": "Native", "metaData": "19:1::7","nativeKind":"[11](AssignStmt)","children":[ + {"@type": "Native", "metaData": "19:1::2","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "19:1::2","name":"x"} + ]}, + {"@type": "Native", "metaData": "19:3::5","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "19:6::7","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "19:6::7","value":"7"} + ]} + ]}, + {"@type": "Native", "metaData": "20:1::8","nativeKind":"[12](AssignStmt)","children":[ + {"@type": "Native", "metaData": "20:1::2","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "20:1::2","name":"x"} + ]}, + {"@type": "Native", "metaData": "20:3::5","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "20:6::8","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "20:6::8","value":"46"} + ]} + ]}, + {"@type": "AssignmentExpression", "metaData": "22:1::9","statementOrExpression": + {"@type": "ParenthesizedExpression", "metaData": "22:5::9","rightParenthesis":"22:8::9","leftParenthesis":"22:5::6","expression": + {"@type": "IntegerLiteral", "metaData": "22:6::8","value":"32"}},"operator":"EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "22:1::2","name":"y"}}, + {"@type": "Native", "metaData": "24:1::43","nativeKind":"[14](AssignStmt)","children":[ + {"@type": "Native", "metaData": "24:1::22","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "24:1::6","name":"cond1"}, + {"@type": "Native", "metaData": "24:6::7"}, + {"@type": "Identifier", "metaData": "24:8::13","name":"cond2"}, + {"@type": "Native", "metaData": "24:13::14"}, + {"@type": "Identifier", "metaData": "24:15::22","name":"string1"} + ]}, + {"@type": "Native", "metaData": "24:22::24","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "24:25::43","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "Literal", "metaData": "24:25::29","value":"true"}, + {"@type": "Native", "metaData": "24:29::30"}, + {"@type": "Literal", "metaData": "24:31::36","value":"false"}, + {"@type": "Native", "metaData": "24:36::37"}, + {"@type": "StringLiteral", "metaData": "24:38::43","value":"\"no!\"","content":"no!"} + ]} + ]}, + {"@type": "Native", "metaData": "25:1::16","nativeKind":"[15](AssignStmt)","children":[ + {"@type": "Native", "metaData": "25:1::7","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "25:1::3","name":"r1"}, + {"@type": "Native", "metaData": "25:3::4"}, + {"@type": "Identifier", "metaData": "25:5::7","name":"r2"} + ]}, + {"@type": "Native", "metaData": "25:8::10","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "25:11::16","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "Native", "metaData": "25:11::16","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "25:11::14","name":"foo"}, + {"@type": "Native", "metaData": "25:14::15","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "25:15::16","nativeKind":"Rparen"} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "26:1::20","nativeKind":"[16](DeclStmt)","children":[ + {"@type": "Native", "metaData": "26:1::20","nativeKind":"Decl(GenDecl)","children":[ + {"@type": "Native", "metaData": "26:1::4","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "26:5::20","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "26:5::20","nativeKind":"[0](ValueSpec)","children":[ + {"@type": "Native", "metaData": "26:5::9","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "26:5::6","name":"i"}, + {"@type": "Native", "metaData": "26:6::7"}, + {"@type": "Identifier", "metaData": "26:8::9","name":"j"} + ]}, + {"@type": "Identifier", "metaData": "26:10::13","name":"int"}, + {"@type": "Native", "metaData": "26:14::15"}, + {"@type": "Native", "metaData": "26:16::20","nativeKind":"Values([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "26:16::17","value":"1"}, + {"@type": "Native", "metaData": "26:17::18"}, + {"@type": "IntegerLiteral", "metaData": "26:19::20","value":"2"} + ]} + ]} + ]} + ]} + ]}, + {"@type": "AssignmentExpression", "metaData": "27:1::12","statementOrExpression": + {"@type": "Native", "metaData": "27:8::12","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "27:8::9","name":"j"}, + {"@type": "Native", "metaData": "27:9::10"}, + {"@type": "Identifier", "metaData": "27:11::12","name":"i"} + ]},"operator":"EQUAL","leftHandSide": + {"@type": "Native", "metaData": "27:1::5","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "27:1::2","name":"i"}, + {"@type": "Native", "metaData": "27:2::3"}, + {"@type": "Identifier", "metaData": "27:4::5","name":"j"} + ]}}, + {"@type": "AssignmentExpression", "metaData": "28:1::36","statementOrExpression": + {"@type": "Native", "metaData": "28:20::36","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "28:20::21","name":"j"}, + {"@type": "Native", "metaData": "28:33::34"}, + {"@type": "Identifier", "metaData": "28:35::36","name":"i"} + ]},"operator":"EQUAL","leftHandSide": + {"@type": "Native", "metaData": "28:1::17","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "28:1::2","name":"i"}, + {"@type": "Native", "metaData": "28:2::3"}, + {"@type": "Identifier", "metaData": "28:16::17","name":"j"} + ]}}, + {"@type": "Native", "metaData": "30:1::16","nativeKind":"[19](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "30:1::16","type": + null,"isVal":true,"initializer": + {"@type": "Native", "metaData": "30:12::16","nativeKind":"[0](BasicLit)"},"identifier": + {"@type": "Identifier", "metaData": "30:7::9","name":"Pi"}} + ]}, + {"@type": "Native", "metaData": "31:1::21","nativeKind":"[20](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "31:1::21","type": + {"@type": "Identifier", "metaData": "31:9::14","name":"float"},"isVal":true,"initializer": + {"@type": "Native", "metaData": "31:17::21","nativeKind":"[0](BasicLit)"},"identifier": + {"@type": "Identifier", "metaData": "31:7::8","name":"g"}} + ]}, + {"@type": "Native", "metaData": "34:4::73","nativeKind":"[21](DeclStmt)","children":[ + {"@type": "Native", "metaData": "34:4::73","nativeKind":"Decl(GenDecl)","children":[ + {"@type": "Native", "metaData": "34:4::7","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "34:20::73","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "34:20::73","nativeKind":"[0](ValueSpec)","children":[ + {"@type": "Native", "metaData": "34:20::49","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "34:20::22","name":"ii"}, + {"@type": "Native", "metaData": "34:34::35"}, + {"@type": "Identifier", "metaData": "34:47::49","name":"jj"} + ]}, + {"@type": "Identifier", "metaData": "34:61::64","name":"int"}, + {"@type": "Native", "metaData": "34:65::66"}, + {"@type": "Native", "metaData": "34:67::73","nativeKind":"Values([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "34:67::69","value":"11"}, + {"@type": "Native", "metaData": "34:69::70"}, + {"@type": "IntegerLiteral", "metaData": "34:71::73","value":"22"} + ]} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "35:4::61","nativeKind":"[22](DeclStmt)","children":[ + {"@type": "Native", "metaData": "35:4::61","nativeKind":"Decl(GenDecl)","children":[ + {"@type": "Native", "metaData": "35:4::7","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "35:8::61","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "35:8::61","nativeKind":"[0](ValueSpec)","children":[ + {"@type": "Native", "metaData": "35:8::14","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "35:8::10","name":"ii"}, + {"@type": "Native", "metaData": "35:10::11"}, + {"@type": "Identifier", "metaData": "35:12::14","name":"jj"} + ]}, + {"@type": "Identifier", "metaData": "35:15::18","name":"int"}, + {"@type": "Native", "metaData": "35:19::20"}, + {"@type": "Native", "metaData": "35:32::61","nativeKind":"Values([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "35:32::34","value":"11"}, + {"@type": "Native", "metaData": "35:46::47"}, + {"@type": "IntegerLiteral", "metaData": "35:59::61","value":"22"} + ]} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "37:4::17","nativeKind":"[23](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "37:4::17","type": + {"@type": "Identifier", "metaData": "37:13::16","name":"int"},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "37:9::12","name":"abc"}} + ]}, + {"@type": "Native", "metaData": "38:4:42:5","nativeKind":"[24](DeclStmt)","children":[ + {"@type": "Native", "metaData": "38:4:42:5","nativeKind":"Decl(GenDecl)","children":[ + {"@type": "Native", "metaData": "38:4::7","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "38:8::9","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "39:8:41:15","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "39:8::14","nativeKind":"[0](ValueSpec)","children":[ + {"@type": "Native", "metaData": "39:8::10","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "39:8::10","name":"aa"} + ]}, + {"@type": "Identifier", "metaData": "39:11::14","name":"int"} + ]}, + {"@type": "Native", "metaData": "40:8::17","nativeKind":"[1](ValueSpec)","children":[ + {"@type": "Native", "metaData": "40:8::10","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "40:8::10","name":"bb"} + ]}, + {"@type": "Identifier", "metaData": "40:11::17","name":"string"} + ]}, + {"@type": "Native", "metaData": "41:8::15","nativeKind":"[2](ValueSpec)","children":[ + {"@type": "Native", "metaData": "41:8::10","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "41:8::10","name":"cc"} + ]}, + {"@type": "Identifier", "metaData": "41:11::15","name":"bool"} + ]} + ]}, + {"@type": "Native", "metaData": "42:4::5","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "45:0:47:1","returnType": + {"@type": "Native", "metaData": "45:11::24","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "45:11::12","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "45:12::15","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "45:12::15","name":"int"} + ]}, + {"@type": "Native", "metaData": "45:15::16"}, + {"@type": "Native", "metaData": "45:17::23","nativeKind":"[1](Field)","children":[ + {"@type": "Identifier", "metaData": "45:17::23","name":"string"} + ]}, + {"@type": "Native", "metaData": "45:23::24","nativeKind":"Closing"} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "45:5::8","name":"foo"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "45:25:47:1","statementOrExpressions":[ + {"@type": "Return", "metaData": "46:1::16","keyword":"46:1::7","body": + {"@type": "Native", "metaData": "46:8::16","nativeKind":"ReturnExprList","children":[ + {"@type": "IntegerLiteral", "metaData": "46:8::10","value":"42"}, + {"@type": "Native", "metaData": "46:10::11"}, + {"@type": "StringLiteral", "metaData": "46:12::16","value":"\"42\"","content":"42"} + ]}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/empty_code.go.source b/sonar-go-to-slang/resources/ast/empty_code.go.source new file mode 100644 index 00000000..05dc188b --- /dev/null +++ b/sonar-go-to-slang/resources/ast/empty_code.go.source @@ -0,0 +1,4 @@ +package ast //This comment should be in the metadata + + + diff --git a/sonar-go-to-slang/resources/ast/empty_code.json b/sonar-go-to-slang/resources/ast/empty_code.json new file mode 100644 index 00000000..7a0b304d --- /dev/null +++ b/sonar-go-to-slang/resources/ast/empty_code.json @@ -0,0 +1,18 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"//This comment should be in the metadata", "contentText":"This comment should be in the metadata", "range":"1:12::52", "contentRange": "1:14::52"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:5:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/function_parameters.go.source b/sonar-go-to-slang/resources/ast/function_parameters.go.source new file mode 100644 index 00000000..07f32898 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/function_parameters.go.source @@ -0,0 +1,38 @@ +package main + +func f1() {} + +func f2(a int) {} + +func f3(a, b int) {} + +func f4(a int, b int) {} + +func f5(a int, b string) {} + +func f6(a int, b, c string) {} + +func f7(a, b int, c, d string) {} + +func f8(a int) int { + return 1 +} + +func f9(a int) (int, string) { + return 1, "one" +} + +func (t *SlangMapper) f10() { } + +func f11(head string, handleFunc func(*store.NodeExtern)) { } + +func g1(i, /*comment*/j int) {} + +func g3(i, j int, /*comment*/ k, l string) {} + +// function literal (lambda) +var g4 = func(a int) int { + // goroutine + go func(){ a++ }() + return a +} \ No newline at end of file diff --git a/sonar-go-to-slang/resources/ast/function_parameters.json b/sonar-go-to-slang/resources/ast/function_parameters.json new file mode 100644 index 00000000..c4120a5a --- /dev/null +++ b/sonar-go-to-slang/resources/ast/function_parameters.json @@ -0,0 +1,418 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"/*comment*/", "contentText":"comment", "range":"29:11::22", "contentRange": "29:13::20"}, + {"text":"/*comment*/", "contentText":"comment", "range":"31:19::30", "contentRange": "31:21::28"}, + {"text":"// function literal (lambda)", "contentText":" function literal (lambda)", "range":"33:0::28", "contentRange": "33:2::28"}, + {"text":"// goroutine", "contentText":" goroutine", "range":"35:4::16", "contentRange": "35:6::16"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"func","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"f1","textRange":"3:5::7"}, + {"text":"(","textRange":"3:7::8"}, + {"text":")","textRange":"3:8::9"}, + {"text":"{","textRange":"3:10::11"}, + {"text":"}","textRange":"3:11::12"}, + {"text":"func","textRange":"5:0::4","type":"KEYWORD"}, + {"text":"f2","textRange":"5:5::7"}, + {"text":"(","textRange":"5:7::8"}, + {"text":"a","textRange":"5:8::9"}, + {"text":"int","textRange":"5:10::13"}, + {"text":")","textRange":"5:13::14"}, + {"text":"{","textRange":"5:15::16"}, + {"text":"}","textRange":"5:16::17"}, + {"text":"func","textRange":"7:0::4","type":"KEYWORD"}, + {"text":"f3","textRange":"7:5::7"}, + {"text":"(","textRange":"7:7::8"}, + {"text":"a","textRange":"7:8::9"}, + {"text":"b","textRange":"7:11::12"}, + {"text":"int","textRange":"7:13::16"}, + {"text":",","textRange":"7:9::10"}, + {"text":")","textRange":"7:16::17"}, + {"text":"{","textRange":"7:18::19"}, + {"text":"}","textRange":"7:19::20"}, + {"text":"func","textRange":"9:0::4","type":"KEYWORD"}, + {"text":"f4","textRange":"9:5::7"}, + {"text":"(","textRange":"9:7::8"}, + {"text":"a","textRange":"9:8::9"}, + {"text":"int","textRange":"9:10::13"}, + {"text":"b","textRange":"9:15::16"}, + {"text":"int","textRange":"9:17::20"}, + {"text":",","textRange":"9:13::14"}, + {"text":")","textRange":"9:20::21"}, + {"text":"{","textRange":"9:22::23"}, + {"text":"}","textRange":"9:23::24"}, + {"text":"func","textRange":"11:0::4","type":"KEYWORD"}, + {"text":"f5","textRange":"11:5::7"}, + {"text":"(","textRange":"11:7::8"}, + {"text":"a","textRange":"11:8::9"}, + {"text":"int","textRange":"11:10::13"}, + {"text":"b","textRange":"11:15::16"}, + {"text":"string","textRange":"11:17::23"}, + {"text":",","textRange":"11:13::14"}, + {"text":")","textRange":"11:23::24"}, + {"text":"{","textRange":"11:25::26"}, + {"text":"}","textRange":"11:26::27"}, + {"text":"func","textRange":"13:0::4","type":"KEYWORD"}, + {"text":"f6","textRange":"13:5::7"}, + {"text":"(","textRange":"13:7::8"}, + {"text":"a","textRange":"13:8::9"}, + {"text":"int","textRange":"13:10::13"}, + {"text":"b","textRange":"13:15::16"}, + {"text":"c","textRange":"13:18::19"}, + {"text":"string","textRange":"13:20::26"}, + {"text":",","textRange":"13:16::17"}, + {"text":",","textRange":"13:13::14"}, + {"text":")","textRange":"13:26::27"}, + {"text":"{","textRange":"13:28::29"}, + {"text":"}","textRange":"13:29::30"}, + {"text":"func","textRange":"15:0::4","type":"KEYWORD"}, + {"text":"f7","textRange":"15:5::7"}, + {"text":"(","textRange":"15:7::8"}, + {"text":"a","textRange":"15:8::9"}, + {"text":"b","textRange":"15:11::12"}, + {"text":"int","textRange":"15:13::16"}, + {"text":",","textRange":"15:9::10"}, + {"text":"c","textRange":"15:18::19"}, + {"text":"d","textRange":"15:21::22"}, + {"text":"string","textRange":"15:23::29"}, + {"text":",","textRange":"15:19::20"}, + {"text":",","textRange":"15:16::17"}, + {"text":")","textRange":"15:29::30"}, + {"text":"{","textRange":"15:31::32"}, + {"text":"}","textRange":"15:32::33"}, + {"text":"func","textRange":"17:0::4","type":"KEYWORD"}, + {"text":"f8","textRange":"17:5::7"}, + {"text":"(","textRange":"17:7::8"}, + {"text":"a","textRange":"17:8::9"}, + {"text":"int","textRange":"17:10::13"}, + {"text":")","textRange":"17:13::14"}, + {"text":"int","textRange":"17:15::18"}, + {"text":"{","textRange":"17:19::20"}, + {"text":"return","textRange":"18:4::10","type":"KEYWORD"}, + {"text":"1","textRange":"18:11::12"}, + {"text":"}","textRange":"19:0::1"}, + {"text":"func","textRange":"21:0::4","type":"KEYWORD"}, + {"text":"f9","textRange":"21:5::7"}, + {"text":"(","textRange":"21:7::8"}, + {"text":"a","textRange":"21:8::9"}, + {"text":"int","textRange":"21:10::13"}, + {"text":")","textRange":"21:13::14"}, + {"text":"(","textRange":"21:15::16"}, + {"text":"int","textRange":"21:16::19"}, + {"text":"string","textRange":"21:21::27"}, + {"text":",","textRange":"21:19::20"}, + {"text":")","textRange":"21:27::28"}, + {"text":"{","textRange":"21:29::30"}, + {"text":"return","textRange":"22:4::10","type":"KEYWORD"}, + {"text":"1","textRange":"22:11::12"}, + {"text":"\"one\"","textRange":"22:14::19","type":"STRING_LITERAL"}, + {"text":",","textRange":"22:12::13"}, + {"text":"}","textRange":"23:0::1"}, + {"text":"func","textRange":"25:0::4","type":"KEYWORD"}, + {"text":"(","textRange":"25:5::6"}, + {"text":"t","textRange":"25:6::7"}, + {"text":"*","textRange":"25:8::9"}, + {"text":"SlangMapper","textRange":"25:9::20"}, + {"text":")","textRange":"25:20::21"}, + {"text":"f10","textRange":"25:22::25"}, + {"text":"(","textRange":"25:25::26"}, + {"text":")","textRange":"25:26::27"}, + {"text":"{","textRange":"25:28::29"}, + {"text":"}","textRange":"25:30::31"}, + {"text":"func","textRange":"27:0::4","type":"KEYWORD"}, + {"text":"f11","textRange":"27:5::8"}, + {"text":"(","textRange":"27:8::9"}, + {"text":"head","textRange":"27:9::13"}, + {"text":"string","textRange":"27:14::20"}, + {"text":"handleFunc","textRange":"27:22::32"}, + {"text":"func","textRange":"27:33::37","type":"KEYWORD"}, + {"text":"(","textRange":"27:37::38"}, + {"text":"*","textRange":"27:38::39"}, + {"text":"store","textRange":"27:39::44"}, + {"text":"NodeExtern","textRange":"27:45::55"}, + {"text":".","textRange":"27:44::45"}, + {"text":")","textRange":"27:55::56"}, + {"text":",","textRange":"27:20::21"}, + {"text":")","textRange":"27:56::57"}, + {"text":"{","textRange":"27:58::59"}, + {"text":"}","textRange":"27:60::61"}, + {"text":"func","textRange":"29:0::4","type":"KEYWORD"}, + {"text":"g1","textRange":"29:5::7"}, + {"text":"(","textRange":"29:7::8"}, + {"text":"i","textRange":"29:8::9"}, + {"text":"j","textRange":"29:22::23"}, + {"text":"int","textRange":"29:24::27"}, + {"text":",","textRange":"29:9::10"}, + {"text":")","textRange":"29:27::28"}, + {"text":"{","textRange":"29:29::30"}, + {"text":"}","textRange":"29:30::31"}, + {"text":"func","textRange":"31:0::4","type":"KEYWORD"}, + {"text":"g3","textRange":"31:5::7"}, + {"text":"(","textRange":"31:7::8"}, + {"text":"i","textRange":"31:8::9"}, + {"text":"j","textRange":"31:11::12"}, + {"text":"int","textRange":"31:14::17"}, + {"text":",","textRange":"31:9::10"}, + {"text":"k","textRange":"31:31::32"}, + {"text":"l","textRange":"31:34::35"}, + {"text":"string","textRange":"31:36::42"}, + {"text":",","textRange":"31:32::33"}, + {"text":",","textRange":"31:17::18"}, + {"text":")","textRange":"31:42::43"}, + {"text":"{","textRange":"31:44::45"}, + {"text":"}","textRange":"31:45::46"}, + {"text":"var","textRange":"34:0::3","type":"KEYWORD"}, + {"text":"g4","textRange":"34:4::6"}, + {"text":"func","textRange":"34:9::13","type":"KEYWORD"}, + {"text":"(","textRange":"34:13::14"}, + {"text":"a","textRange":"34:14::15"}, + {"text":"int","textRange":"34:16::19"}, + {"text":")","textRange":"34:19::20"}, + {"text":"int","textRange":"34:21::24"}, + {"text":"{","textRange":"34:25::26"}, + {"text":"go","textRange":"36:4::6","type":"KEYWORD"}, + {"text":"func","textRange":"36:7::11","type":"KEYWORD"}, + {"text":"(","textRange":"36:11::12"}, + {"text":")","textRange":"36:12::13"}, + {"text":"{","textRange":"36:13::14"}, + {"text":"a","textRange":"36:15::16"}, + {"text":"++","textRange":"36:16::18"}, + {"text":"}","textRange":"36:19::20"}, + {"text":"(","textRange":"36:20::21"}, + {"text":")","textRange":"36:21::22"}, + {"text":"return","textRange":"37:4::10","type":"KEYWORD"}, + {"text":"a","textRange":"37:11::12"}, + {"text":"}","textRange":"38:0::1"}, + {"text":"=","textRange":"34:7::8"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:38:1","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "3:0::12","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "3:5::7","name":"f1"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "3:10::12","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "5:0::17","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "5:5::7","name":"f2"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "5:8::13","type": + {"@type": "Identifier", "metaData": "5:10::13","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "5:8::9","name":"a"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "5:15::17","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "7:0::20","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "7:5::7","name":"f3"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "7:8::9","type": + null,"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "7:8::9","name":"a"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "7:11::16","type": + {"@type": "Identifier", "metaData": "7:13::16","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "7:11::12","name":"b"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "7:18::20","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "9:0::24","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "9:5::7","name":"f4"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "9:8::13","type": + {"@type": "Identifier", "metaData": "9:10::13","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "9:8::9","name":"a"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "9:15::20","type": + {"@type": "Identifier", "metaData": "9:17::20","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "9:15::16","name":"b"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "9:22::24","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "11:0::27","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "11:5::7","name":"f5"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "11:8::13","type": + {"@type": "Identifier", "metaData": "11:10::13","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "11:8::9","name":"a"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "11:15::23","type": + {"@type": "Identifier", "metaData": "11:17::23","name":"string"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "11:15::16","name":"b"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "11:25::27","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "13:0::30","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "13:5::7","name":"f6"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "13:8::13","type": + {"@type": "Identifier", "metaData": "13:10::13","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "13:8::9","name":"a"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "13:15::16","type": + null,"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "13:15::16","name":"b"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "13:18::26","type": + {"@type": "Identifier", "metaData": "13:20::26","name":"string"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "13:18::19","name":"c"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "13:28::30","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "15:0::33","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "15:5::7","name":"f7"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "15:8::9","type": + null,"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "15:8::9","name":"a"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "15:11::16","type": + {"@type": "Identifier", "metaData": "15:13::16","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "15:11::12","name":"b"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "15:18::19","type": + null,"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "15:18::19","name":"c"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "15:21::29","type": + {"@type": "Identifier", "metaData": "15:23::29","name":"string"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "15:21::22","name":"d"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "15:31::33","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "17:0:19:1","returnType": + {"@type": "Native", "metaData": "17:15::18","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "17:15::18","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "17:15::18","name":"int"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "17:5::7","name":"f8"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "17:8::13","type": + {"@type": "Identifier", "metaData": "17:10::13","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "17:8::9","name":"a"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "17:19:19:1","statementOrExpressions":[ + {"@type": "Return", "metaData": "18:4::12","keyword":"18:4::10","body": + {"@type": "IntegerLiteral", "metaData": "18:11::12","value":"1"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "21:0:23:1","returnType": + {"@type": "Native", "metaData": "21:15::28","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "21:15::16","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "21:16::19","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "21:16::19","name":"int"} + ]}, + {"@type": "Native", "metaData": "21:19::20"}, + {"@type": "Native", "metaData": "21:21::27","nativeKind":"[1](Field)","children":[ + {"@type": "Identifier", "metaData": "21:21::27","name":"string"} + ]}, + {"@type": "Native", "metaData": "21:27::28","nativeKind":"Closing"} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "21:5::7","name":"f9"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "21:8::13","type": + {"@type": "Identifier", "metaData": "21:10::13","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "21:8::9","name":"a"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "21:29:23:1","statementOrExpressions":[ + {"@type": "Return", "metaData": "22:4::19","keyword":"22:4::10","body": + {"@type": "Native", "metaData": "22:11::19","nativeKind":"ReturnExprList","children":[ + {"@type": "IntegerLiteral", "metaData": "22:11::12","value":"1"}, + {"@type": "Native", "metaData": "22:12::13"}, + {"@type": "StringLiteral", "metaData": "22:14::19","value":"\"one\"","content":"one"} + ]}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "25:0::31","returnType": + null,"nativeChildren":[ + {"@type": "Native", "metaData": "25:5::21","nativeKind":"Recv(FieldList)","children":[ + {"@type": "Native", "metaData": "25:5::6","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "25:6::20","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "25:6::7","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "25:6::7","name":"t"} + ]}, + {"@type": "Native", "metaData": "25:8::20","nativeKind":"Type(StarExpr)","children":[ + {"@type": "Native", "metaData": "25:8::9","nativeKind":"Star"}, + {"@type": "Identifier", "metaData": "25:9::20","name":"SlangMapper"} + ]} + ]}, + {"@type": "Native", "metaData": "25:20::21","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "25:22::25","name":"f10"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "25:28::31","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "27:0::61","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "27:5::8","name":"f11"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "27:9::20","type": + {"@type": "Identifier", "metaData": "27:14::20","name":"string"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "27:9::13","name":"head"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "27:22::56","type": + {"@type": "Native", "metaData": "27:33::56","nativeKind":"Type(FuncType)","children":[ + {"@type": "Native", "metaData": "27:33::37","nativeKind":"Func"}, + {"@type": "Native", "metaData": "27:37::56","nativeKind":"Params(FieldList)","children":[ + {"@type": "Native", "metaData": "27:37::38","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "27:38::55","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "27:38::55","nativeKind":"Type(StarExpr)","children":[ + {"@type": "Native", "metaData": "27:38::39","nativeKind":"Star"}, + {"@type": "Native", "metaData": "27:39::55","nativeKind":"X(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "27:39::44","name":"store"}, + {"@type": "Native", "metaData": "27:44::45"}, + {"@type": "Identifier", "metaData": "27:45::55","name":"NodeExtern"} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "27:55::56","nativeKind":"Closing"} + ]} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "27:22::32","name":"handleFunc"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "27:58::61","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "29:0::31","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "29:5::7","name":"g1"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "29:8::9","type": + null,"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "29:8::9","name":"i"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "29:22::27","type": + {"@type": "Identifier", "metaData": "29:24::27","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "29:22::23","name":"j"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "29:29::31","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "31:0::46","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "31:5::7","name":"g3"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "31:8::9","type": + null,"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "31:8::9","name":"i"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "31:11::17","type": + {"@type": "Identifier", "metaData": "31:14::17","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "31:11::12","name":"j"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "31:31::32","type": + null,"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "31:31::32","name":"k"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "31:34::42","type": + {"@type": "Identifier", "metaData": "31:36::42","name":"string"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "31:34::35","name":"l"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "31:44::46","statementOrExpressions":[]}}, + {"@type": "VariableDeclaration", "metaData": "34:0:38:1","type": + null,"isVal":false,"initializer": + {"@type": "FunctionDeclaration", "metaData": "34:9:38:1","returnType": + {"@type": "Native", "metaData": "34:21::24","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "34:21::24","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "34:21::24","name":"int"} + ]} + ]},"nativeChildren":null,"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "34:14::19","type": + {"@type": "Identifier", "metaData": "34:16::19","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "34:14::15","name":"a"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "34:25:38:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "36:4::22","nativeKind":"[0](GoStmt)","children":[ + {"@type": "Native", "metaData": "36:4::6","nativeKind":"Go"}, + {"@type": "Native", "metaData": "36:7::22","nativeKind":"Call(CallExpr)","children":[ + {"@type": "FunctionDeclaration", "metaData": "36:7::20","returnType": + null,"nativeChildren":null,"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "36:13::20","statementOrExpressions":[ + {"@type": "UnaryExpression", "metaData": "36:15::18","operator":"INCREMENT","operand": + {"@type": "Identifier", "metaData": "36:15::16","name":"a"}} + ]}}, + {"@type": "Native", "metaData": "36:20::21","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "36:21::22","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Return", "metaData": "37:4::12","keyword":"37:4::10","body": + {"@type": "Identifier", "metaData": "37:11::12","name":"a"}} + ]}},"identifier": + {"@type": "Identifier", "metaData": "34:4::6","name":"g4"}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/generics.go.source b/sonar-go-to-slang/resources/ast/generics.go.source new file mode 100644 index 00000000..8747e27d --- /dev/null +++ b/sonar-go-to-slang/resources/ast/generics.go.source @@ -0,0 +1,90 @@ +package main + +import "fmt" + +type M[T any] []T + +func f1[P any]() { +} +func f2[S interface{ ~[]byte|string }]() { +} +func f3[S ~[]E, E any]() { +} +func f4[P M[int]]() { +} +func f5[_ any]() { +} + +// Code taken from https://go.dev/doc/tutorial/generics + +type Number interface { + int64 | float64 +} + +func main() { + // Initialize a map for the integer values + ints := map[string]int64{ + "first": 34, + "second": 12, + } + + // Initialize a map for the float values + floats := map[string]float64{ + "first": 35.98, + "second": 26.99, + } + + fmt.Printf("Non-Generic Sums: %v and %v\n", + SumInts(ints), + SumFloats(floats)) + + fmt.Printf("Generic Sums: %v and %v\n", + SumIntsOrFloats[string, int64](ints), + SumIntsOrFloats[string, float64](floats)) + + fmt.Printf("Generic Sums, type parameters inferred: %v and %v\n", + SumIntsOrFloats(ints), + SumIntsOrFloats(floats)) + + fmt.Printf("Generic Sums with Constraint: %v and %v\n", + SumNumbers(ints), + SumNumbers(floats)) +} + +// SumInts adds together the values of m. +func SumInts(m map[string]int64) int64 { + var s int64 + for _, v := range m { + s += v + } + return s +} + +// SumFloats adds together the values of m. +func SumFloats(m map[string]float64) float64 { + var s float64 + for _, v := range m { + s += v + } + return s +} + +// SumIntsOrFloats sums the values of map m. It supports both floats and integers +// as map values. +func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V { + var s V + for _, v := range m { + s += v + } + return s +} + +// SumNumbers sums the values of map m. Its supports both integers +// and floats as map values. +func SumNumbers[K comparable, V Number](m map[K]V) V { + var s V + for _, v := range m { + s += v + } + return s +} diff --git a/sonar-go-to-slang/resources/ast/generics.json b/sonar-go-to-slang/resources/ast/generics.json new file mode 100644 index 00000000..d2275738 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/generics.json @@ -0,0 +1,926 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// Code taken from https://go.dev/doc/tutorial/generics", "contentText":" Code taken from https://go.dev/doc/tutorial/generics", "range":"18:0::55", "contentRange": "18:2::55"}, + {"text":"// Initialize a map for the integer values", "contentText":" Initialize a map for the integer values", "range":"25:4::46", "contentRange": "25:6::46"}, + {"text":"// Initialize a map for the float values", "contentText":" Initialize a map for the float values", "range":"31:4::44", "contentRange": "31:6::44"}, + {"text":"// SumInts adds together the values of m.", "contentText":" SumInts adds together the values of m.", "range":"54:0::41", "contentRange": "54:2::41"}, + {"text":"// SumFloats adds together the values of m.", "contentText":" SumFloats adds together the values of m.", "range":"63:0::43", "contentRange": "63:2::43"}, + {"text":"// SumIntsOrFloats sums the values of map m. It supports both floats and integers", "contentText":" SumIntsOrFloats sums the values of map m. It supports both floats and integers", "range":"72:0::81", "contentRange": "72:2::81"}, + {"text":"// as map values.", "contentText":" as map values.", "range":"73:0::17", "contentRange": "73:2::17"}, + {"text":"// SumNumbers sums the values of map m. Its supports both integers", "contentText":" SumNumbers sums the values of map m. Its supports both integers", "range":"82:0::66", "contentRange": "82:2::66"}, + {"text":"// and floats as map values.", "contentText":" and floats as map values.", "range":"83:0::28", "contentRange": "83:2::28"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"import","textRange":"3:0::6","type":"KEYWORD"}, + {"text":"\"fmt\"","textRange":"3:7::12","type":"STRING_LITERAL"}, + {"text":"type","textRange":"5:0::4","type":"KEYWORD"}, + {"text":"M","textRange":"5:5::6"}, + {"text":"[","textRange":"5:6::7"}, + {"text":"T","textRange":"5:7::8"}, + {"text":"any","textRange":"5:9::12"}, + {"text":"]","textRange":"5:12::13"}, + {"text":"[","textRange":"5:14::15"}, + {"text":"T","textRange":"5:16::17"}, + {"text":"]","textRange":"5:15::16"}, + {"text":"func","textRange":"7:0::4","type":"KEYWORD"}, + {"text":"f1","textRange":"7:5::7"}, + {"text":"[","textRange":"7:7::8"}, + {"text":"P","textRange":"7:8::9"}, + {"text":"any","textRange":"7:10::13"}, + {"text":"]","textRange":"7:13::14"}, + {"text":"(","textRange":"7:14::15"}, + {"text":")","textRange":"7:15::16"}, + {"text":"{","textRange":"7:17::18"}, + {"text":"}","textRange":"8:0::1"}, + {"text":"func","textRange":"9:0::4","type":"KEYWORD"}, + {"text":"f2","textRange":"9:5::7"}, + {"text":"[","textRange":"9:7::8"}, + {"text":"S","textRange":"9:8::9"}, + {"text":"interface","textRange":"9:10::19","type":"KEYWORD"}, + {"text":"{","textRange":"9:19::20"}, + {"text":"~","textRange":"9:21::22"}, + {"text":"[","textRange":"9:22::23"}, + {"text":"byte","textRange":"9:24::28"}, + {"text":"]","textRange":"9:23::24"}, + {"text":"|","textRange":"9:28::29"}, + {"text":"string","textRange":"9:29::35"}, + {"text":"}","textRange":"9:36::37"}, + {"text":"]","textRange":"9:37::38"}, + {"text":"(","textRange":"9:38::39"}, + {"text":")","textRange":"9:39::40"}, + {"text":"{","textRange":"9:41::42"}, + {"text":"}","textRange":"10:0::1"}, + {"text":"func","textRange":"11:0::4","type":"KEYWORD"}, + {"text":"f3","textRange":"11:5::7"}, + {"text":"[","textRange":"11:7::8"}, + {"text":"S","textRange":"11:8::9"}, + {"text":"~","textRange":"11:10::11"}, + {"text":"[","textRange":"11:11::12"}, + {"text":"E","textRange":"11:13::14"}, + {"text":"]","textRange":"11:12::13"}, + {"text":"E","textRange":"11:16::17"}, + {"text":"any","textRange":"11:18::21"}, + {"text":",","textRange":"11:14::15"}, + {"text":"]","textRange":"11:21::22"}, + {"text":"(","textRange":"11:22::23"}, + {"text":")","textRange":"11:23::24"}, + {"text":"{","textRange":"11:25::26"}, + {"text":"}","textRange":"12:0::1"}, + {"text":"func","textRange":"13:0::4","type":"KEYWORD"}, + {"text":"f4","textRange":"13:5::7"}, + {"text":"[","textRange":"13:7::8"}, + {"text":"P","textRange":"13:8::9"}, + {"text":"M","textRange":"13:10::11"}, + {"text":"[","textRange":"13:11::12"}, + {"text":"int","textRange":"13:12::15"}, + {"text":"]","textRange":"13:15::16"}, + {"text":"]","textRange":"13:16::17"}, + {"text":"(","textRange":"13:17::18"}, + {"text":")","textRange":"13:18::19"}, + {"text":"{","textRange":"13:20::21"}, + {"text":"}","textRange":"14:0::1"}, + {"text":"func","textRange":"15:0::4","type":"KEYWORD"}, + {"text":"f5","textRange":"15:5::7"}, + {"text":"[","textRange":"15:7::8"}, + {"text":"_","textRange":"15:8::9","type":"KEYWORD"}, + {"text":"any","textRange":"15:10::13"}, + {"text":"]","textRange":"15:13::14"}, + {"text":"(","textRange":"15:14::15"}, + {"text":")","textRange":"15:15::16"}, + {"text":"{","textRange":"15:17::18"}, + {"text":"}","textRange":"16:0::1"}, + {"text":"type","textRange":"20:0::4","type":"KEYWORD"}, + {"text":"Number","textRange":"20:5::11"}, + {"text":"interface","textRange":"20:12::21","type":"KEYWORD"}, + {"text":"{","textRange":"20:22::23"}, + {"text":"int64","textRange":"21:4::9"}, + {"text":"|","textRange":"21:10::11"}, + {"text":"float64","textRange":"21:12::19"}, + {"text":"}","textRange":"22:0::1"}, + {"text":"func","textRange":"24:0::4","type":"KEYWORD"}, + {"text":"main","textRange":"24:5::9"}, + {"text":"(","textRange":"24:9::10"}, + {"text":")","textRange":"24:10::11"}, + {"text":"{","textRange":"24:12::13"}, + {"text":"ints","textRange":"26:4::8"}, + {"text":":=","textRange":"26:9::11"}, + {"text":"map","textRange":"26:12::15","type":"KEYWORD"}, + {"text":"string","textRange":"26:16::22"}, + {"text":"[","textRange":"26:15::16"}, + {"text":"int64","textRange":"26:23::28"}, + {"text":"]","textRange":"26:22::23"}, + {"text":"{","textRange":"26:28::29"}, + {"text":"\"first\"","textRange":"27:8::15","type":"STRING_LITERAL"}, + {"text":":","textRange":"27:15::16"}, + {"text":"34","textRange":"27:17::19"}, + {"text":"\"second\"","textRange":"28:8::16","type":"STRING_LITERAL"}, + {"text":":","textRange":"28:16::17"}, + {"text":"12","textRange":"28:18::20"}, + {"text":",","textRange":"27:19::20"}, + {"text":"}","textRange":"29:4::5"}, + {"text":",","textRange":"28:20::21"}, + {"text":"floats","textRange":"32:4::10"}, + {"text":":=","textRange":"32:11::13"}, + {"text":"map","textRange":"32:14::17","type":"KEYWORD"}, + {"text":"string","textRange":"32:18::24"}, + {"text":"[","textRange":"32:17::18"}, + {"text":"float64","textRange":"32:25::32"}, + {"text":"]","textRange":"32:24::25"}, + {"text":"{","textRange":"32:32::33"}, + {"text":"\"first\"","textRange":"33:8::15","type":"STRING_LITERAL"}, + {"text":":","textRange":"33:15::16"}, + {"text":"35.98","textRange":"33:17::22"}, + {"text":"\"second\"","textRange":"34:8::16","type":"STRING_LITERAL"}, + {"text":":","textRange":"34:16::17"}, + {"text":"26.99","textRange":"34:18::23"}, + {"text":",","textRange":"33:22::23"}, + {"text":"}","textRange":"35:4::5"}, + {"text":",","textRange":"34:23::24"}, + {"text":"fmt","textRange":"37:4::7"}, + {"text":"Printf","textRange":"37:8::14"}, + {"text":".","textRange":"37:7::8"}, + {"text":"(","textRange":"37:14::15"}, + {"text":"\"Non-Generic Sums: %v and %v\\n\"","textRange":"37:15::46","type":"STRING_LITERAL"}, + {"text":"SumInts","textRange":"38:8::15"}, + {"text":"(","textRange":"38:15::16"}, + {"text":"ints","textRange":"38:16::20"}, + {"text":")","textRange":"38:20::21"}, + {"text":",","textRange":"37:46::47"}, + {"text":"SumFloats","textRange":"39:8::17"}, + {"text":"(","textRange":"39:17::18"}, + {"text":"floats","textRange":"39:18::24"}, + {"text":")","textRange":"39:24::25"}, + {"text":",","textRange":"38:21::22"}, + {"text":")","textRange":"39:25::26"}, + {"text":"fmt","textRange":"41:4::7"}, + {"text":"Printf","textRange":"41:8::14"}, + {"text":".","textRange":"41:7::8"}, + {"text":"(","textRange":"41:14::15"}, + {"text":"\"Generic Sums: %v and %v\\n\"","textRange":"41:15::42","type":"STRING_LITERAL"}, + {"text":"SumIntsOrFloats","textRange":"42:8::23"}, + {"text":"[","textRange":"42:23::24"}, + {"text":"string","textRange":"42:24::30"}, + {"text":"int64","textRange":"42:32::37"}, + {"text":",","textRange":"42:30::31"}, + {"text":"]","textRange":"42:37::38"}, + {"text":"(","textRange":"42:38::39"}, + {"text":"ints","textRange":"42:39::43"}, + {"text":")","textRange":"42:43::44"}, + {"text":",","textRange":"41:42::43"}, + {"text":"SumIntsOrFloats","textRange":"43:8::23"}, + {"text":"[","textRange":"43:23::24"}, + {"text":"string","textRange":"43:24::30"}, + {"text":"float64","textRange":"43:32::39"}, + {"text":",","textRange":"43:30::31"}, + {"text":"]","textRange":"43:39::40"}, + {"text":"(","textRange":"43:40::41"}, + {"text":"floats","textRange":"43:41::47"}, + {"text":")","textRange":"43:47::48"}, + {"text":",","textRange":"42:44::45"}, + {"text":")","textRange":"43:48::49"}, + {"text":"fmt","textRange":"45:4::7"}, + {"text":"Printf","textRange":"45:8::14"}, + {"text":".","textRange":"45:7::8"}, + {"text":"(","textRange":"45:14::15"}, + {"text":"\"Generic Sums, type parameters inferred: %v and %v\\n\"","textRange":"45:15::68","type":"STRING_LITERAL"}, + {"text":"SumIntsOrFloats","textRange":"46:8::23"}, + {"text":"(","textRange":"46:23::24"}, + {"text":"ints","textRange":"46:24::28"}, + {"text":")","textRange":"46:28::29"}, + {"text":",","textRange":"45:68::69"}, + {"text":"SumIntsOrFloats","textRange":"47:8::23"}, + {"text":"(","textRange":"47:23::24"}, + {"text":"floats","textRange":"47:24::30"}, + {"text":")","textRange":"47:30::31"}, + {"text":",","textRange":"46:29::30"}, + {"text":")","textRange":"47:31::32"}, + {"text":"fmt","textRange":"49:4::7"}, + {"text":"Printf","textRange":"49:8::14"}, + {"text":".","textRange":"49:7::8"}, + {"text":"(","textRange":"49:14::15"}, + {"text":"\"Generic Sums with Constraint: %v and %v\\n\"","textRange":"49:15::58","type":"STRING_LITERAL"}, + {"text":"SumNumbers","textRange":"50:8::18"}, + {"text":"(","textRange":"50:18::19"}, + {"text":"ints","textRange":"50:19::23"}, + {"text":")","textRange":"50:23::24"}, + {"text":",","textRange":"49:58::59"}, + {"text":"SumNumbers","textRange":"51:8::18"}, + {"text":"(","textRange":"51:18::19"}, + {"text":"floats","textRange":"51:19::25"}, + {"text":")","textRange":"51:25::26"}, + {"text":",","textRange":"50:24::25"}, + {"text":")","textRange":"51:26::27"}, + {"text":"}","textRange":"52:0::1"}, + {"text":"func","textRange":"55:0::4","type":"KEYWORD"}, + {"text":"SumInts","textRange":"55:5::12"}, + {"text":"(","textRange":"55:12::13"}, + {"text":"m","textRange":"55:13::14"}, + {"text":"map","textRange":"55:15::18","type":"KEYWORD"}, + {"text":"string","textRange":"55:19::25"}, + {"text":"[","textRange":"55:18::19"}, + {"text":"int64","textRange":"55:26::31"}, + {"text":"]","textRange":"55:25::26"}, + {"text":")","textRange":"55:31::32"}, + {"text":"int64","textRange":"55:33::38"}, + {"text":"{","textRange":"55:39::40"}, + {"text":"var","textRange":"56:4::7","type":"KEYWORD"}, + {"text":"s","textRange":"56:8::9"}, + {"text":"int64","textRange":"56:10::15"}, + {"text":"for","textRange":"57:4::7","type":"KEYWORD"}, + {"text":"_","textRange":"57:8::9","type":"KEYWORD"}, + {"text":"v","textRange":"57:11::12"}, + {"text":",","textRange":"57:9::10"}, + {"text":":=","textRange":"57:13::15"}, + {"text":"m","textRange":"57:22::23"}, + {"text":"range","textRange":"57:16::21","type":"KEYWORD"}, + {"text":"{","textRange":"57:24::25"}, + {"text":"s","textRange":"58:8::9"}, + {"text":"+=","textRange":"58:10::12"}, + {"text":"v","textRange":"58:13::14"}, + {"text":"}","textRange":"59:4::5"}, + {"text":"return","textRange":"60:4::10","type":"KEYWORD"}, + {"text":"s","textRange":"60:11::12"}, + {"text":"}","textRange":"61:0::1"}, + {"text":"func","textRange":"64:0::4","type":"KEYWORD"}, + {"text":"SumFloats","textRange":"64:5::14"}, + {"text":"(","textRange":"64:14::15"}, + {"text":"m","textRange":"64:15::16"}, + {"text":"map","textRange":"64:17::20","type":"KEYWORD"}, + {"text":"string","textRange":"64:21::27"}, + {"text":"[","textRange":"64:20::21"}, + {"text":"float64","textRange":"64:28::35"}, + {"text":"]","textRange":"64:27::28"}, + {"text":")","textRange":"64:35::36"}, + {"text":"float64","textRange":"64:37::44"}, + {"text":"{","textRange":"64:45::46"}, + {"text":"var","textRange":"65:4::7","type":"KEYWORD"}, + {"text":"s","textRange":"65:8::9"}, + {"text":"float64","textRange":"65:10::17"}, + {"text":"for","textRange":"66:4::7","type":"KEYWORD"}, + {"text":"_","textRange":"66:8::9","type":"KEYWORD"}, + {"text":"v","textRange":"66:11::12"}, + {"text":",","textRange":"66:9::10"}, + {"text":":=","textRange":"66:13::15"}, + {"text":"m","textRange":"66:22::23"}, + {"text":"range","textRange":"66:16::21","type":"KEYWORD"}, + {"text":"{","textRange":"66:24::25"}, + {"text":"s","textRange":"67:8::9"}, + {"text":"+=","textRange":"67:10::12"}, + {"text":"v","textRange":"67:13::14"}, + {"text":"}","textRange":"68:4::5"}, + {"text":"return","textRange":"69:4::10","type":"KEYWORD"}, + {"text":"s","textRange":"69:11::12"}, + {"text":"}","textRange":"70:0::1"}, + {"text":"func","textRange":"74:0::4","type":"KEYWORD"}, + {"text":"SumIntsOrFloats","textRange":"74:5::20"}, + {"text":"[","textRange":"74:20::21"}, + {"text":"K","textRange":"74:21::22"}, + {"text":"comparable","textRange":"74:23::33"}, + {"text":"V","textRange":"74:35::36"}, + {"text":"int64","textRange":"74:37::42"}, + {"text":"|","textRange":"74:43::44"}, + {"text":"float64","textRange":"74:45::52"}, + {"text":",","textRange":"74:33::34"}, + {"text":"]","textRange":"74:52::53"}, + {"text":"(","textRange":"74:53::54"}, + {"text":"m","textRange":"74:54::55"}, + {"text":"map","textRange":"74:56::59","type":"KEYWORD"}, + {"text":"K","textRange":"74:60::61"}, + {"text":"[","textRange":"74:59::60"}, + {"text":"V","textRange":"74:62::63"}, + {"text":"]","textRange":"74:61::62"}, + {"text":")","textRange":"74:63::64"}, + {"text":"V","textRange":"74:65::66"}, + {"text":"{","textRange":"74:67::68"}, + {"text":"var","textRange":"75:4::7","type":"KEYWORD"}, + {"text":"s","textRange":"75:8::9"}, + {"text":"V","textRange":"75:10::11"}, + {"text":"for","textRange":"76:4::7","type":"KEYWORD"}, + {"text":"_","textRange":"76:8::9","type":"KEYWORD"}, + {"text":"v","textRange":"76:11::12"}, + {"text":",","textRange":"76:9::10"}, + {"text":":=","textRange":"76:13::15"}, + {"text":"m","textRange":"76:22::23"}, + {"text":"range","textRange":"76:16::21","type":"KEYWORD"}, + {"text":"{","textRange":"76:24::25"}, + {"text":"s","textRange":"77:8::9"}, + {"text":"+=","textRange":"77:10::12"}, + {"text":"v","textRange":"77:13::14"}, + {"text":"}","textRange":"78:4::5"}, + {"text":"return","textRange":"79:4::10","type":"KEYWORD"}, + {"text":"s","textRange":"79:11::12"}, + {"text":"}","textRange":"80:0::1"}, + {"text":"func","textRange":"84:0::4","type":"KEYWORD"}, + {"text":"SumNumbers","textRange":"84:5::15"}, + {"text":"[","textRange":"84:15::16"}, + {"text":"K","textRange":"84:16::17"}, + {"text":"comparable","textRange":"84:18::28"}, + {"text":"V","textRange":"84:30::31"}, + {"text":"Number","textRange":"84:32::38"}, + {"text":",","textRange":"84:28::29"}, + {"text":"]","textRange":"84:38::39"}, + {"text":"(","textRange":"84:39::40"}, + {"text":"m","textRange":"84:40::41"}, + {"text":"map","textRange":"84:42::45","type":"KEYWORD"}, + {"text":"K","textRange":"84:46::47"}, + {"text":"[","textRange":"84:45::46"}, + {"text":"V","textRange":"84:48::49"}, + {"text":"]","textRange":"84:47::48"}, + {"text":")","textRange":"84:49::50"}, + {"text":"V","textRange":"84:51::52"}, + {"text":"{","textRange":"84:53::54"}, + {"text":"var","textRange":"85:4::7","type":"KEYWORD"}, + {"text":"s","textRange":"85:8::9"}, + {"text":"V","textRange":"85:10::11"}, + {"text":"for","textRange":"86:4::7","type":"KEYWORD"}, + {"text":"_","textRange":"86:8::9","type":"KEYWORD"}, + {"text":"v","textRange":"86:11::12"}, + {"text":",","textRange":"86:9::10"}, + {"text":":=","textRange":"86:13::15"}, + {"text":"m","textRange":"86:22::23"}, + {"text":"range","textRange":"86:16::21","type":"KEYWORD"}, + {"text":"{","textRange":"86:24::25"}, + {"text":"s","textRange":"87:8::9"}, + {"text":"+=","textRange":"87:10::12"}, + {"text":"v","textRange":"87:13::14"}, + {"text":"}","textRange":"88:4::5"}, + {"text":"return","textRange":"89:4::10","type":"KEYWORD"}, + {"text":"s","textRange":"89:11::12"}, + {"text":"}","textRange":"90:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:91:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "ImportDeclaration", "metaData": "3:0::12","children":[ + {"@type": "Native", "metaData": "3:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "3:7::12","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "3:7::12","value":"\"fmt\"","content":"fmt"} + ]} + ]}, + {"@type": "ClassDeclaration", "metaData": "5:0::17","identifier":"5:5::6","classTree": + {"@type": "Native", "metaData": "5:0::17","nativeKind":"[1](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "5:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "5:5::6","name":"M"}, + {"@type": "Native", "metaData": "5:6::13","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "5:6::7","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "5:7::12","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "5:7::8","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "5:7::8","name":"T"} + ]}, + {"@type": "Identifier", "metaData": "5:9::12","name":"any"} + ]}, + {"@type": "Native", "metaData": "5:12::13","nativeKind":"Closing"} + ]}, + {"@type": "Native", "metaData": "5:14::17","nativeKind":"Type(ArrayType)","children":[ + {"@type": "Native", "metaData": "5:14::15","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "5:15::16"}, + {"@type": "Identifier", "metaData": "5:16::17","name":"T"} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "7:0:8:1","returnType": + null,"nativeChildren":[ + {"@type": "Native", "metaData": "7:7::14","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "7:7::8","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "7:8::13","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "7:8::9","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "7:8::9","name":"P"} + ]}, + {"@type": "Identifier", "metaData": "7:10::13","name":"any"} + ]}, + {"@type": "Native", "metaData": "7:13::14","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "7:5::7","name":"f1"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "7:17:8:1","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "9:0:10:1","returnType": + null,"nativeChildren":[ + {"@type": "Native", "metaData": "9:7::38","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "9:7::8","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "9:8::37","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "9:8::9","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "9:8::9","name":"S"} + ]}, + {"@type": "Native", "metaData": "9:10::37","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "9:10::19","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "9:19::37","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "9:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "9:21::35","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "9:21::35","nativeKind":"Type(BinaryExpr)","children":[ + {"@type": "Native", "metaData": "9:21::28","nativeKind":"X(UnaryExpr)","children":[ + {"@type": "Native", "metaData": "9:21::22","nativeKind":"Op"}, + {"@type": "Native", "metaData": "9:22::28","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "9:22::23","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "9:23::24"}, + {"@type": "Identifier", "metaData": "9:24::28","name":"byte"} + ]} + ]}, + {"@type": "Native", "metaData": "9:28::29","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "9:29::35","name":"string"} + ]} + ]}, + {"@type": "Native", "metaData": "9:36::37","nativeKind":"Closing"} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "9:37::38","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "9:5::7","name":"f2"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "9:41:10:1","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "11:0:12:1","returnType": + null,"nativeChildren":[ + {"@type": "Native", "metaData": "11:7::22","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "11:7::8","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "11:8::14","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "11:8::9","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "11:8::9","name":"S"} + ]}, + {"@type": "Native", "metaData": "11:10::14","nativeKind":"Type(UnaryExpr)","children":[ + {"@type": "Native", "metaData": "11:10::11","nativeKind":"Op"}, + {"@type": "Native", "metaData": "11:11::14","nativeKind":"X(ArrayType)","children":[ + {"@type": "Native", "metaData": "11:11::12","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "11:12::13"}, + {"@type": "Identifier", "metaData": "11:13::14","name":"E"} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "11:14::15"}, + {"@type": "Native", "metaData": "11:16::21","nativeKind":"[1](Field)","children":[ + {"@type": "Native", "metaData": "11:16::17","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "11:16::17","name":"E"} + ]}, + {"@type": "Identifier", "metaData": "11:18::21","name":"any"} + ]}, + {"@type": "Native", "metaData": "11:21::22","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "11:5::7","name":"f3"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "11:25:12:1","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "13:0:14:1","returnType": + null,"nativeChildren":[ + {"@type": "Native", "metaData": "13:7::17","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "13:7::8","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "13:8::16","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "13:8::9","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "13:8::9","name":"P"} + ]}, + {"@type": "Native", "metaData": "13:10::16","nativeKind":"Type(IndexExpr)","children":[ + {"@type": "Identifier", "metaData": "13:10::11","name":"M"}, + {"@type": "Native", "metaData": "13:11::12","nativeKind":"Lbrack"}, + {"@type": "Identifier", "metaData": "13:12::15","name":"int"}, + {"@type": "Native", "metaData": "13:15::16","nativeKind":"Rbrack"} + ]} + ]}, + {"@type": "Native", "metaData": "13:16::17","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "13:5::7","name":"f4"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "13:20:14:1","statementOrExpressions":[]}}, + {"@type": "FunctionDeclaration", "metaData": "15:0:16:1","returnType": + null,"nativeChildren":[ + {"@type": "Native", "metaData": "15:7::14","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "15:7::8","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "15:8::13","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "15:8::9","nativeKind":"Names([]*Ident)","children":[ + {"@type": "PlaceHolder", "metaData": "15:8::9","placeHolderToken":"15:8::9"} + ]}, + {"@type": "Identifier", "metaData": "15:10::13","name":"any"} + ]}, + {"@type": "Native", "metaData": "15:13::14","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "15:5::7","name":"f5"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "15:17:16:1","statementOrExpressions":[]}}, + {"@type": "ClassDeclaration", "metaData": "20:0:22:1","identifier":"20:5::11","classTree": + {"@type": "Native", "metaData": "20:0:22:1","nativeKind":"[7](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "20:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "20:5::11","name":"Number"}, + {"@type": "Native", "metaData": "20:12:22:1","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "20:12::21","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "20:22:22:1","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "20:22::23","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "21:4::19","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "21:4::19","nativeKind":"Type(BinaryExpr)","children":[ + {"@type": "Identifier", "metaData": "21:4::9","name":"int64"}, + {"@type": "Native", "metaData": "21:10::11","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "21:12::19","name":"float64"} + ]} + ]}, + {"@type": "Native", "metaData": "22:0::1","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "24:0:52:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "24:5::9","name":"main"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "24:12:52:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "26:4:29:5","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "26:12:29:5","nativeKind":"[0](CompositeLit)","children":[ + {"@type": "Native", "metaData": "26:12::28","nativeKind":"Type(MapType)","children":[ + {"@type": "Native", "metaData": "26:12::15","nativeKind":"Map"}, + {"@type": "Native", "metaData": "26:15::16"}, + {"@type": "Identifier", "metaData": "26:16::22","name":"string"}, + {"@type": "Native", "metaData": "26:22::23"}, + {"@type": "Identifier", "metaData": "26:23::28","name":"int64"} + ]}, + {"@type": "Native", "metaData": "26:28::29","nativeKind":"Lbrace"}, + {"@type": "Native", "metaData": "27:8:28:20","nativeKind":"Elts([]Expr)","children":[ + {"@type": "Native", "metaData": "27:8::19","nativeKind":"[0](KeyValueExpr)","children":[ + {"@type": "StringLiteral", "metaData": "27:8::15","value":"\"first\"","content":"first"}, + {"@type": "Native", "metaData": "27:15::16","nativeKind":"Colon"}, + {"@type": "IntegerLiteral", "metaData": "27:17::19","value":"34"} + ]}, + {"@type": "Native", "metaData": "27:19::20"}, + {"@type": "Native", "metaData": "28:8::20","nativeKind":"[1](KeyValueExpr)","children":[ + {"@type": "StringLiteral", "metaData": "28:8::16","value":"\"second\"","content":"second"}, + {"@type": "Native", "metaData": "28:16::17","nativeKind":"Colon"}, + {"@type": "IntegerLiteral", "metaData": "28:18::20","value":"12"} + ]} + ]}, + {"@type": "Native", "metaData": "28:20::21"}, + {"@type": "Native", "metaData": "29:4::5","nativeKind":"Rbrace"} + ]},"identifier": + {"@type": "Identifier", "metaData": "26:4::8","name":"ints"}}, + {"@type": "VariableDeclaration", "metaData": "32:4:35:5","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "32:14:35:5","nativeKind":"[0](CompositeLit)","children":[ + {"@type": "Native", "metaData": "32:14::32","nativeKind":"Type(MapType)","children":[ + {"@type": "Native", "metaData": "32:14::17","nativeKind":"Map"}, + {"@type": "Native", "metaData": "32:17::18"}, + {"@type": "Identifier", "metaData": "32:18::24","name":"string"}, + {"@type": "Native", "metaData": "32:24::25"}, + {"@type": "Identifier", "metaData": "32:25::32","name":"float64"} + ]}, + {"@type": "Native", "metaData": "32:32::33","nativeKind":"Lbrace"}, + {"@type": "Native", "metaData": "33:8:34:23","nativeKind":"Elts([]Expr)","children":[ + {"@type": "Native", "metaData": "33:8::22","nativeKind":"[0](KeyValueExpr)","children":[ + {"@type": "StringLiteral", "metaData": "33:8::15","value":"\"first\"","content":"first"}, + {"@type": "Native", "metaData": "33:15::16","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "33:17::22","nativeKind":"Value(BasicLit)"} + ]}, + {"@type": "Native", "metaData": "33:22::23"}, + {"@type": "Native", "metaData": "34:8::23","nativeKind":"[1](KeyValueExpr)","children":[ + {"@type": "StringLiteral", "metaData": "34:8::16","value":"\"second\"","content":"second"}, + {"@type": "Native", "metaData": "34:16::17","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "34:18::23","nativeKind":"Value(BasicLit)"} + ]} + ]}, + {"@type": "Native", "metaData": "34:23::24"}, + {"@type": "Native", "metaData": "35:4::5","nativeKind":"Rbrace"} + ]},"identifier": + {"@type": "Identifier", "metaData": "32:4::10","name":"floats"}}, + {"@type": "Native", "metaData": "37:4:39:26","nativeKind":"[2](ExprStmt)","children":[ + {"@type": "Native", "metaData": "37:4:39:26","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "37:4::14","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "37:4::7","name":"fmt"}, + {"@type": "Native", "metaData": "37:7::8"}, + {"@type": "Identifier", "metaData": "37:8::14","name":"Printf"} + ]}, + {"@type": "Native", "metaData": "37:14::15","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "37:15:39:25","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "37:15::46","value":"\"Non-Generic Sums: %v and %v\\n\"","content":"Non-Generic Sums: %v and %v\\n"}, + {"@type": "Native", "metaData": "37:46::47"}, + {"@type": "Native", "metaData": "38:8::21","nativeKind":"[1](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "38:8::15","name":"SumInts"}, + {"@type": "Native", "metaData": "38:15::16","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "38:16::20","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "38:16::20","name":"ints"} + ]}, + {"@type": "Native", "metaData": "38:20::21","nativeKind":"Rparen"} + ]}, + {"@type": "Native", "metaData": "38:21::22"}, + {"@type": "Native", "metaData": "39:8::25","nativeKind":"[2](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "39:8::17","name":"SumFloats"}, + {"@type": "Native", "metaData": "39:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "39:18::24","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "39:18::24","name":"floats"} + ]}, + {"@type": "Native", "metaData": "39:24::25","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "39:25::26","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "41:4:43:49","nativeKind":"[3](ExprStmt)","children":[ + {"@type": "Native", "metaData": "41:4:43:49","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "41:4::14","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "41:4::7","name":"fmt"}, + {"@type": "Native", "metaData": "41:7::8"}, + {"@type": "Identifier", "metaData": "41:8::14","name":"Printf"} + ]}, + {"@type": "Native", "metaData": "41:14::15","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "41:15:43:48","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "41:15::42","value":"\"Generic Sums: %v and %v\\n\"","content":"Generic Sums: %v and %v\\n"}, + {"@type": "Native", "metaData": "41:42::43"}, + {"@type": "Native", "metaData": "42:8::44","nativeKind":"[1](CallExpr)","children":[ + {"@type": "Native", "metaData": "42:8::38","nativeKind":"Fun(IndexListExpr)","children":[ + {"@type": "Identifier", "metaData": "42:8::23","name":"SumIntsOrFloats"}, + {"@type": "Native", "metaData": "42:23::24","nativeKind":"Lbrack"}, + {"@type": "Identifier", "metaData": "42:24::30","name":"string"}, + {"@type": "Native", "metaData": "42:30::31"}, + {"@type": "Identifier", "metaData": "42:32::37","name":"int64"}, + {"@type": "Native", "metaData": "42:37::38","nativeKind":"Rbrack"} + ]}, + {"@type": "Native", "metaData": "42:38::39","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "42:39::43","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "42:39::43","name":"ints"} + ]}, + {"@type": "Native", "metaData": "42:43::44","nativeKind":"Rparen"} + ]}, + {"@type": "Native", "metaData": "42:44::45"}, + {"@type": "Native", "metaData": "43:8::48","nativeKind":"[2](CallExpr)","children":[ + {"@type": "Native", "metaData": "43:8::40","nativeKind":"Fun(IndexListExpr)","children":[ + {"@type": "Identifier", "metaData": "43:8::23","name":"SumIntsOrFloats"}, + {"@type": "Native", "metaData": "43:23::24","nativeKind":"Lbrack"}, + {"@type": "Identifier", "metaData": "43:24::30","name":"string"}, + {"@type": "Native", "metaData": "43:30::31"}, + {"@type": "Identifier", "metaData": "43:32::39","name":"float64"}, + {"@type": "Native", "metaData": "43:39::40","nativeKind":"Rbrack"} + ]}, + {"@type": "Native", "metaData": "43:40::41","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "43:41::47","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "43:41::47","name":"floats"} + ]}, + {"@type": "Native", "metaData": "43:47::48","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "43:48::49","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "45:4:47:32","nativeKind":"[4](ExprStmt)","children":[ + {"@type": "Native", "metaData": "45:4:47:32","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "45:4::14","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "45:4::7","name":"fmt"}, + {"@type": "Native", "metaData": "45:7::8"}, + {"@type": "Identifier", "metaData": "45:8::14","name":"Printf"} + ]}, + {"@type": "Native", "metaData": "45:14::15","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "45:15:47:31","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "45:15::68","value":"\"Generic Sums, type parameters inferred: %v and %v\\n\"","content":"Generic Sums, type parameters inferred: %v and %v\\n"}, + {"@type": "Native", "metaData": "45:68::69"}, + {"@type": "Native", "metaData": "46:8::29","nativeKind":"[1](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "46:8::23","name":"SumIntsOrFloats"}, + {"@type": "Native", "metaData": "46:23::24","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "46:24::28","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "46:24::28","name":"ints"} + ]}, + {"@type": "Native", "metaData": "46:28::29","nativeKind":"Rparen"} + ]}, + {"@type": "Native", "metaData": "46:29::30"}, + {"@type": "Native", "metaData": "47:8::31","nativeKind":"[2](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "47:8::23","name":"SumIntsOrFloats"}, + {"@type": "Native", "metaData": "47:23::24","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "47:24::30","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "47:24::30","name":"floats"} + ]}, + {"@type": "Native", "metaData": "47:30::31","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "47:31::32","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "49:4:51:27","nativeKind":"[5](ExprStmt)","children":[ + {"@type": "Native", "metaData": "49:4:51:27","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "49:4::14","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "49:4::7","name":"fmt"}, + {"@type": "Native", "metaData": "49:7::8"}, + {"@type": "Identifier", "metaData": "49:8::14","name":"Printf"} + ]}, + {"@type": "Native", "metaData": "49:14::15","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "49:15:51:26","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "49:15::58","value":"\"Generic Sums with Constraint: %v and %v\\n\"","content":"Generic Sums with Constraint: %v and %v\\n"}, + {"@type": "Native", "metaData": "49:58::59"}, + {"@type": "Native", "metaData": "50:8::24","nativeKind":"[1](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "50:8::18","name":"SumNumbers"}, + {"@type": "Native", "metaData": "50:18::19","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "50:19::23","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "50:19::23","name":"ints"} + ]}, + {"@type": "Native", "metaData": "50:23::24","nativeKind":"Rparen"} + ]}, + {"@type": "Native", "metaData": "50:24::25"}, + {"@type": "Native", "metaData": "51:8::26","nativeKind":"[2](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "51:8::18","name":"SumNumbers"}, + {"@type": "Native", "metaData": "51:18::19","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "51:19::25","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "51:19::25","name":"floats"} + ]}, + {"@type": "Native", "metaData": "51:25::26","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "51:26::27","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "55:0:61:1","returnType": + {"@type": "Native", "metaData": "55:33::38","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "55:33::38","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "55:33::38","name":"int64"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "55:5::12","name":"SumInts"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "55:13::31","type": + {"@type": "Native", "metaData": "55:15::31","nativeKind":"Type(MapType)","children":[ + {"@type": "Native", "metaData": "55:15::18","nativeKind":"Map"}, + {"@type": "Native", "metaData": "55:18::19"}, + {"@type": "Identifier", "metaData": "55:19::25","name":"string"}, + {"@type": "Native", "metaData": "55:25::26"}, + {"@type": "Identifier", "metaData": "55:26::31","name":"int64"} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "55:13::14","name":"m"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "55:39:61:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "56:4::15","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "56:4::15","type": + {"@type": "Identifier", "metaData": "56:10::15","name":"int64"},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "56:8::9","name":"s"}} + ]}, + {"@type": "Loop", "metaData": "57:4:59:5","kind":"FOR","keyword":"57:4::7","condition": + {"@type": "Native", "metaData": "57:8::23","nativeKind":"RangeHeader","children":[ + {"@type": "PlaceHolder", "metaData": "57:8::9","placeHolderToken":"57:8::9"}, + {"@type": "Native", "metaData": "57:9::10"}, + {"@type": "Identifier", "metaData": "57:11::12","name":"v"}, + {"@type": "Native", "metaData": "57:13::15","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "57:16::21"}, + {"@type": "Identifier", "metaData": "57:22::23","name":"m"} + ]},"body": + {"@type": "Block", "metaData": "57:24:59:5","statementOrExpressions":[ + {"@type": "AssignmentExpression", "metaData": "58:8::14","statementOrExpression": + {"@type": "Identifier", "metaData": "58:13::14","name":"v"},"operator":"PLUS_EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "58:8::9","name":"s"}} + ]}}, + {"@type": "Return", "metaData": "60:4::12","keyword":"60:4::10","body": + {"@type": "Identifier", "metaData": "60:11::12","name":"s"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "64:0:70:1","returnType": + {"@type": "Native", "metaData": "64:37::44","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "64:37::44","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "64:37::44","name":"float64"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "64:5::14","name":"SumFloats"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "64:15::35","type": + {"@type": "Native", "metaData": "64:17::35","nativeKind":"Type(MapType)","children":[ + {"@type": "Native", "metaData": "64:17::20","nativeKind":"Map"}, + {"@type": "Native", "metaData": "64:20::21"}, + {"@type": "Identifier", "metaData": "64:21::27","name":"string"}, + {"@type": "Native", "metaData": "64:27::28"}, + {"@type": "Identifier", "metaData": "64:28::35","name":"float64"} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "64:15::16","name":"m"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "64:45:70:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "65:4::17","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "65:4::17","type": + {"@type": "Identifier", "metaData": "65:10::17","name":"float64"},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "65:8::9","name":"s"}} + ]}, + {"@type": "Loop", "metaData": "66:4:68:5","kind":"FOR","keyword":"66:4::7","condition": + {"@type": "Native", "metaData": "66:8::23","nativeKind":"RangeHeader","children":[ + {"@type": "PlaceHolder", "metaData": "66:8::9","placeHolderToken":"66:8::9"}, + {"@type": "Native", "metaData": "66:9::10"}, + {"@type": "Identifier", "metaData": "66:11::12","name":"v"}, + {"@type": "Native", "metaData": "66:13::15","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "66:16::21"}, + {"@type": "Identifier", "metaData": "66:22::23","name":"m"} + ]},"body": + {"@type": "Block", "metaData": "66:24:68:5","statementOrExpressions":[ + {"@type": "AssignmentExpression", "metaData": "67:8::14","statementOrExpression": + {"@type": "Identifier", "metaData": "67:13::14","name":"v"},"operator":"PLUS_EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "67:8::9","name":"s"}} + ]}}, + {"@type": "Return", "metaData": "69:4::12","keyword":"69:4::10","body": + {"@type": "Identifier", "metaData": "69:11::12","name":"s"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "74:0:80:1","returnType": + {"@type": "Native", "metaData": "74:65::66","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "74:65::66","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "74:65::66","name":"V"} + ]} + ]},"nativeChildren":[ + {"@type": "Native", "metaData": "74:20::53","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "74:20::21","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "74:21::33","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "74:21::22","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "74:21::22","name":"K"} + ]}, + {"@type": "Identifier", "metaData": "74:23::33","name":"comparable"} + ]}, + {"@type": "Native", "metaData": "74:33::34"}, + {"@type": "Native", "metaData": "74:35::52","nativeKind":"[1](Field)","children":[ + {"@type": "Native", "metaData": "74:35::36","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "74:35::36","name":"V"} + ]}, + {"@type": "Native", "metaData": "74:37::52","nativeKind":"Type(BinaryExpr)","children":[ + {"@type": "Identifier", "metaData": "74:37::42","name":"int64"}, + {"@type": "Native", "metaData": "74:43::44","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "74:45::52","name":"float64"} + ]} + ]}, + {"@type": "Native", "metaData": "74:52::53","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "74:5::20","name":"SumIntsOrFloats"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "74:54::63","type": + {"@type": "Native", "metaData": "74:56::63","nativeKind":"Type(MapType)","children":[ + {"@type": "Native", "metaData": "74:56::59","nativeKind":"Map"}, + {"@type": "Native", "metaData": "74:59::60"}, + {"@type": "Identifier", "metaData": "74:60::61","name":"K"}, + {"@type": "Native", "metaData": "74:61::62"}, + {"@type": "Identifier", "metaData": "74:62::63","name":"V"} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "74:54::55","name":"m"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "74:67:80:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "75:4::11","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "75:4::11","type": + {"@type": "Identifier", "metaData": "75:10::11","name":"V"},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "75:8::9","name":"s"}} + ]}, + {"@type": "Loop", "metaData": "76:4:78:5","kind":"FOR","keyword":"76:4::7","condition": + {"@type": "Native", "metaData": "76:8::23","nativeKind":"RangeHeader","children":[ + {"@type": "PlaceHolder", "metaData": "76:8::9","placeHolderToken":"76:8::9"}, + {"@type": "Native", "metaData": "76:9::10"}, + {"@type": "Identifier", "metaData": "76:11::12","name":"v"}, + {"@type": "Native", "metaData": "76:13::15","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "76:16::21"}, + {"@type": "Identifier", "metaData": "76:22::23","name":"m"} + ]},"body": + {"@type": "Block", "metaData": "76:24:78:5","statementOrExpressions":[ + {"@type": "AssignmentExpression", "metaData": "77:8::14","statementOrExpression": + {"@type": "Identifier", "metaData": "77:13::14","name":"v"},"operator":"PLUS_EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "77:8::9","name":"s"}} + ]}}, + {"@type": "Return", "metaData": "79:4::12","keyword":"79:4::10","body": + {"@type": "Identifier", "metaData": "79:11::12","name":"s"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "84:0:90:1","returnType": + {"@type": "Native", "metaData": "84:51::52","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "84:51::52","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "84:51::52","name":"V"} + ]} + ]},"nativeChildren":[ + {"@type": "Native", "metaData": "84:15::39","nativeKind":"TypeParams(FieldList)","children":[ + {"@type": "Native", "metaData": "84:15::16","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "84:16::28","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "84:16::17","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "84:16::17","name":"K"} + ]}, + {"@type": "Identifier", "metaData": "84:18::28","name":"comparable"} + ]}, + {"@type": "Native", "metaData": "84:28::29"}, + {"@type": "Native", "metaData": "84:30::38","nativeKind":"[1](Field)","children":[ + {"@type": "Native", "metaData": "84:30::31","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "84:30::31","name":"V"} + ]}, + {"@type": "Identifier", "metaData": "84:32::38","name":"Number"} + ]}, + {"@type": "Native", "metaData": "84:38::39","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "84:5::15","name":"SumNumbers"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "84:40::49","type": + {"@type": "Native", "metaData": "84:42::49","nativeKind":"Type(MapType)","children":[ + {"@type": "Native", "metaData": "84:42::45","nativeKind":"Map"}, + {"@type": "Native", "metaData": "84:45::46"}, + {"@type": "Identifier", "metaData": "84:46::47","name":"K"}, + {"@type": "Native", "metaData": "84:47::48"}, + {"@type": "Identifier", "metaData": "84:48::49","name":"V"} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "84:40::41","name":"m"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "84:53:90:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "85:4::11","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "85:4::11","type": + {"@type": "Identifier", "metaData": "85:10::11","name":"V"},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "85:8::9","name":"s"}} + ]}, + {"@type": "Loop", "metaData": "86:4:88:5","kind":"FOR","keyword":"86:4::7","condition": + {"@type": "Native", "metaData": "86:8::23","nativeKind":"RangeHeader","children":[ + {"@type": "PlaceHolder", "metaData": "86:8::9","placeHolderToken":"86:8::9"}, + {"@type": "Native", "metaData": "86:9::10"}, + {"@type": "Identifier", "metaData": "86:11::12","name":"v"}, + {"@type": "Native", "metaData": "86:13::15","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "86:16::21"}, + {"@type": "Identifier", "metaData": "86:22::23","name":"m"} + ]},"body": + {"@type": "Block", "metaData": "86:24:88:5","statementOrExpressions":[ + {"@type": "AssignmentExpression", "metaData": "87:8::14","statementOrExpression": + {"@type": "Identifier", "metaData": "87:13::14","name":"v"},"operator":"PLUS_EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "87:8::9","name":"s"}} + ]}}, + {"@type": "Return", "metaData": "89:4::12","keyword":"89:4::10","body": + {"@type": "Identifier", "metaData": "89:11::12","name":"s"}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/hello_world.go.source b/sonar-go-to-slang/resources/ast/hello_world.go.source new file mode 100644 index 00000000..4bf209b7 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/hello_world.go.source @@ -0,0 +1,10 @@ +// +build ignore + +package main + +import "fmt" + +func main() { + msg := "hello, world" + fmt.Println(msg) +} diff --git a/sonar-go-to-slang/resources/ast/hello_world.json b/sonar-go-to-slang/resources/ast/hello_world.json new file mode 100644 index 00000000..46fc6dbf --- /dev/null +++ b/sonar-go-to-slang/resources/ast/hello_world.json @@ -0,0 +1,63 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// +build ignore", "contentText":" +build ignore", "range":"1:0::16", "contentRange": "1:2::16"} + ], + "tokens": [ + {"text":"package","textRange":"3:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"3:8::12"}, + {"text":"import","textRange":"5:0::6","type":"KEYWORD"}, + {"text":"\"fmt\"","textRange":"5:7::12","type":"STRING_LITERAL"}, + {"text":"func","textRange":"7:0::4","type":"KEYWORD"}, + {"text":"main","textRange":"7:5::9"}, + {"text":"(","textRange":"7:9::10"}, + {"text":")","textRange":"7:10::11"}, + {"text":"{","textRange":"7:12::13"}, + {"text":"msg","textRange":"8:1::4"}, + {"text":":=","textRange":"8:5::7"}, + {"text":"\"hello, world\"","textRange":"8:8::22","type":"STRING_LITERAL"}, + {"text":"fmt","textRange":"9:1::4"}, + {"text":"Println","textRange":"9:5::12"}, + {"text":".","textRange":"9:4::5"}, + {"text":"(","textRange":"9:12::13"}, + {"text":"msg","textRange":"9:13::16"}, + {"text":")","textRange":"9:16::17"}, + {"text":"}","textRange":"10:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:11:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0:3:12","children":[ + {"@type": "Native", "metaData": "3:0::7"}, + {"@type": "Identifier", "metaData": "3:8::12","name":"main"} + ]}, + {"@type": "ImportDeclaration", "metaData": "5:0::12","children":[ + {"@type": "Native", "metaData": "5:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "5:7::12","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "5:7::12","value":"\"fmt\"","content":"fmt"} + ]} + ]}, + {"@type": "FunctionDeclaration", "metaData": "7:0:10:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "7:5::9","name":"main"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "7:12:10:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "8:1::22","type":null,"isVal":false,"initializer": + {"@type": "StringLiteral", "metaData": "8:8::22","value":"\"hello, world\"","content":"hello, world"},"identifier": + {"@type": "Identifier", "metaData": "8:1::4","name":"msg"}}, + {"@type": "Native", "metaData": "9:1::17","nativeKind":"[1](ExprStmt)","children":[ + {"@type": "Native", "metaData": "9:1::17","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "9:1::12","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "9:1::4","name":"fmt"}, + {"@type": "Native", "metaData": "9:4::5"}, + {"@type": "Identifier", "metaData": "9:5::12","name":"Println"} + ]}, + {"@type": "Native", "metaData": "9:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "9:13::16","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "9:13::16","name":"msg"} + ]}, + {"@type": "Native", "metaData": "9:16::17","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/if.go.source b/sonar-go-to-slang/resources/ast/if.go.source new file mode 100644 index 00000000..3e0ad55d --- /dev/null +++ b/sonar-go-to-slang/resources/ast/if.go.source @@ -0,0 +1,23 @@ +package main + +import "fmt" + +func main() { + if true { + fmt.Println("thenPart") + } + + if false { + fmt.Println("thenPart") + } else { + fmt.Println("elsePart") + } + + if (true) { + fmt.Println("thenPart") + } + + if x := 42; true && x > 0 { + fmt.Println("thenPart") + } +} diff --git a/sonar-go-to-slang/resources/ast/if.json b/sonar-go-to-slang/resources/ast/if.json new file mode 100644 index 00000000..f03bbe29 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/if.json @@ -0,0 +1,197 @@ +{ + "treeMetaData": { + "comments": [ + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"import","textRange":"3:0::6","type":"KEYWORD"}, + {"text":"\"fmt\"","textRange":"3:7::12","type":"STRING_LITERAL"}, + {"text":"func","textRange":"5:0::4","type":"KEYWORD"}, + {"text":"main","textRange":"5:5::9"}, + {"text":"(","textRange":"5:9::10"}, + {"text":")","textRange":"5:10::11"}, + {"text":"{","textRange":"5:12::13"}, + {"text":"if","textRange":"6:1::3","type":"KEYWORD"}, + {"text":"true","textRange":"6:4::8"}, + {"text":"{","textRange":"6:9::10"}, + {"text":"fmt","textRange":"7:2::5"}, + {"text":"Println","textRange":"7:6::13"}, + {"text":".","textRange":"7:5::6"}, + {"text":"(","textRange":"7:13::14"}, + {"text":"\"thenPart\"","textRange":"7:14::24","type":"STRING_LITERAL"}, + {"text":")","textRange":"7:24::25"}, + {"text":"}","textRange":"8:1::2"}, + {"text":"if","textRange":"10:1::3","type":"KEYWORD"}, + {"text":"false","textRange":"10:4::9"}, + {"text":"{","textRange":"10:10::11"}, + {"text":"fmt","textRange":"11:2::5"}, + {"text":"Println","textRange":"11:6::13"}, + {"text":".","textRange":"11:5::6"}, + {"text":"(","textRange":"11:13::14"}, + {"text":"\"thenPart\"","textRange":"11:14::24","type":"STRING_LITERAL"}, + {"text":")","textRange":"11:24::25"}, + {"text":"}","textRange":"12:1::2"}, + {"text":"{","textRange":"12:8::9"}, + {"text":"fmt","textRange":"13:2::5"}, + {"text":"Println","textRange":"13:6::13"}, + {"text":".","textRange":"13:5::6"}, + {"text":"(","textRange":"13:13::14"}, + {"text":"\"elsePart\"","textRange":"13:14::24","type":"STRING_LITERAL"}, + {"text":")","textRange":"13:24::25"}, + {"text":"}","textRange":"14:1::2"}, + {"text":"else","textRange":"12:3::7","type":"KEYWORD"}, + {"text":"if","textRange":"16:1::3","type":"KEYWORD"}, + {"text":"(","textRange":"16:4::5"}, + {"text":"true","textRange":"16:5::9"}, + {"text":")","textRange":"16:9::10"}, + {"text":"{","textRange":"16:11::12"}, + {"text":"fmt","textRange":"17:2::5"}, + {"text":"Println","textRange":"17:6::13"}, + {"text":".","textRange":"17:5::6"}, + {"text":"(","textRange":"17:13::14"}, + {"text":"\"thenPart\"","textRange":"17:14::24","type":"STRING_LITERAL"}, + {"text":")","textRange":"17:24::25"}, + {"text":"}","textRange":"18:1::2"}, + {"text":"if","textRange":"20:1::3","type":"KEYWORD"}, + {"text":"x","textRange":"20:4::5"}, + {"text":":=","textRange":"20:6::8"}, + {"text":"42","textRange":"20:9::11"}, + {"text":"true","textRange":"20:13::17"}, + {"text":"\u0026\u0026","textRange":"20:18::20"}, + {"text":"x","textRange":"20:21::22"}, + {"text":"\u003e","textRange":"20:23::24"}, + {"text":"0","textRange":"20:25::26"}, + {"text":";","textRange":"20:11::12"}, + {"text":"{","textRange":"20:27::28"}, + {"text":"fmt","textRange":"21:8::11"}, + {"text":"Println","textRange":"21:12::19"}, + {"text":".","textRange":"21:11::12"}, + {"text":"(","textRange":"21:19::20"}, + {"text":"\"thenPart\"","textRange":"21:20::30","type":"STRING_LITERAL"}, + {"text":")","textRange":"21:30::31"}, + {"text":"}","textRange":"22:4::5"}, + {"text":"}","textRange":"23:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:24:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "ImportDeclaration", "metaData": "3:0::12","children":[ + {"@type": "Native", "metaData": "3:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "3:7::12","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "3:7::12","value":"\"fmt\"","content":"fmt"} + ]} + ]}, + {"@type": "FunctionDeclaration", "metaData": "5:0:23:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "5:5::9","name":"main"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "5:12:23:1","statementOrExpressions":[ + {"@type": "If", "metaData": "6:1:8:2","thenBranch": + {"@type": "Block", "metaData": "6:9:8:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "7:2::25","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "7:2::25","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "7:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "7:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "7:5::6"}, + {"@type": "Identifier", "metaData": "7:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "7:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "7:14::24","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "7:14::24","value":"\"thenPart\"","content":"thenPart"} + ]}, + {"@type": "Native", "metaData": "7:24::25","nativeKind":"Rparen"} + ]} + ]} + ]},"ifKeyword":"6:1::3","elseKeyword":null,"elseBranch": + null,"condition": + {"@type": "Literal", "metaData": "6:4::8","value":"true"}}, + {"@type": "If", "metaData": "10:1:14:2","thenBranch": + {"@type": "Block", "metaData": "10:10:12:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "11:2::25","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "11:2::25","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "11:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "11:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "11:5::6"}, + {"@type": "Identifier", "metaData": "11:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "11:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "11:14::24","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "11:14::24","value":"\"thenPart\"","content":"thenPart"} + ]}, + {"@type": "Native", "metaData": "11:24::25","nativeKind":"Rparen"} + ]} + ]} + ]},"ifKeyword":"10:1::3","elseKeyword":"12:3::7","elseBranch": + {"@type": "Block", "metaData": "12:8:14:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "13:2::25","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "13:2::25","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "13:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "13:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "13:5::6"}, + {"@type": "Identifier", "metaData": "13:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "13:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "13:14::24","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "13:14::24","value":"\"elsePart\"","content":"elsePart"} + ]}, + {"@type": "Native", "metaData": "13:24::25","nativeKind":"Rparen"} + ]} + ]} + ]},"condition": + {"@type": "Literal", "metaData": "10:4::9","value":"false"}}, + {"@type": "If", "metaData": "16:1:18:2","thenBranch": + {"@type": "Block", "metaData": "16:11:18:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "17:2::25","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "17:2::25","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "17:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "17:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "17:5::6"}, + {"@type": "Identifier", "metaData": "17:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "17:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "17:14::24","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "17:14::24","value":"\"thenPart\"","content":"thenPart"} + ]}, + {"@type": "Native", "metaData": "17:24::25","nativeKind":"Rparen"} + ]} + ]} + ]},"ifKeyword":"16:1::3","elseKeyword":null,"elseBranch": + null,"condition": + {"@type": "ParenthesizedExpression", "metaData": "16:4::10","rightParenthesis":"16:9::10","leftParenthesis":"16:4::5","expression": + {"@type": "Literal", "metaData": "16:5::9","value":"true"}}}, + {"@type": "If", "metaData": "20:1:22:5","thenBranch": + {"@type": "Block", "metaData": "20:27:22:5","statementOrExpressions":[ + {"@type": "Native", "metaData": "21:8::31","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "21:8::31","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "21:8::19","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "21:8::11","name":"fmt"}, + {"@type": "Native", "metaData": "21:11::12"}, + {"@type": "Identifier", "metaData": "21:12::19","name":"Println"} + ]}, + {"@type": "Native", "metaData": "21:19::20","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "21:20::30","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "21:20::30","value":"\"thenPart\"","content":"thenPart"} + ]}, + {"@type": "Native", "metaData": "21:30::31","nativeKind":"Rparen"} + ]} + ]} + ]},"ifKeyword":"20:1::3","elseKeyword":null,"elseBranch": + null,"condition": + {"@type": "Native", "metaData": "20:4::26","nativeKind":"InitAndCond","children":[ + {"@type": "VariableDeclaration", "metaData": "20:4::11","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "20:9::11","value":"42"},"identifier": + {"@type": "Identifier", "metaData": "20:4::5","name":"x"}}, + {"@type": "Native", "metaData": "20:11::12","nativeKind":"Semicolon"}, + {"@type": "BinaryExpression", "metaData": "20:13::26","rightOperand": + {"@type": "BinaryExpression", "metaData": "20:21::26","rightOperand": + {"@type": "IntegerLiteral", "metaData": "20:25::26","value":"0"},"operatorToken":"20:23::24","operator":"GREATER_THAN","leftOperand": + {"@type": "Identifier", "metaData": "20:21::22","name":"x"}},"operatorToken":"20:18::20","operator":"CONDITIONAL_AND","leftOperand": + {"@type": "Literal", "metaData": "20:13::17","value":"true"}} + ]}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/lets.go.source b/sonar-go-to-slang/resources/ast/lets.go.source new file mode 100644 index 00000000..6c4eb5b3 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/lets.go.source @@ -0,0 +1,20 @@ +// This is not a line of code +package main +import "fmt" +type class1 struct { x, y int } +type class2 struct { a, b string } +type anyObject interface {} +func fun1() { + fmt.Println("Statement 1") +} +func fun2(i int) { + switch i { // Statement 2 + case 2: + fmt.Println( + "Statement 3", + ) + } +} +func fun3(x interface{}) int { + return 42 // Statement 4 +} \ No newline at end of file diff --git a/sonar-go-to-slang/resources/ast/lets.json b/sonar-go-to-slang/resources/ast/lets.json new file mode 100644 index 00000000..b50c4924 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/lets.json @@ -0,0 +1,225 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// This is not a line of code", "contentText":" This is not a line of code", "range":"1:0::29", "contentRange": "1:2::29"}, + {"text":"// Statement 2", "contentText":" Statement 2", "range":"11:13::27", "contentRange": "11:15::27"}, + {"text":"// Statement 4", "contentText":" Statement 4", "range":"19:12::26", "contentRange": "19:14::26"} + ], + "tokens": [ + {"text":"package","textRange":"2:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"2:8::12"}, + {"text":"import","textRange":"3:0::6","type":"KEYWORD"}, + {"text":"\"fmt\"","textRange":"3:7::12","type":"STRING_LITERAL"}, + {"text":"type","textRange":"4:0::4","type":"KEYWORD"}, + {"text":"class1","textRange":"4:5::11"}, + {"text":"struct","textRange":"4:12::18","type":"KEYWORD"}, + {"text":"{","textRange":"4:19::20"}, + {"text":"x","textRange":"4:21::22"}, + {"text":"y","textRange":"4:24::25"}, + {"text":",","textRange":"4:22::23"}, + {"text":"int","textRange":"4:26::29"}, + {"text":"}","textRange":"4:30::31"}, + {"text":"type","textRange":"5:0::4","type":"KEYWORD"}, + {"text":"class2","textRange":"5:5::11"}, + {"text":"struct","textRange":"5:12::18","type":"KEYWORD"}, + {"text":"{","textRange":"5:19::20"}, + {"text":"a","textRange":"5:21::22"}, + {"text":"b","textRange":"5:24::25"}, + {"text":",","textRange":"5:22::23"}, + {"text":"string","textRange":"5:26::32"}, + {"text":"}","textRange":"5:33::34"}, + {"text":"type","textRange":"6:0::4","type":"KEYWORD"}, + {"text":"anyObject","textRange":"6:5::14"}, + {"text":"interface","textRange":"6:15::24","type":"KEYWORD"}, + {"text":"{","textRange":"6:25::26"}, + {"text":"}","textRange":"6:26::27"}, + {"text":"func","textRange":"7:0::4","type":"KEYWORD"}, + {"text":"fun1","textRange":"7:5::9"}, + {"text":"(","textRange":"7:9::10"}, + {"text":")","textRange":"7:10::11"}, + {"text":"{","textRange":"7:12::13"}, + {"text":"fmt","textRange":"8:2::5"}, + {"text":"Println","textRange":"8:6::13"}, + {"text":".","textRange":"8:5::6"}, + {"text":"(","textRange":"8:13::14"}, + {"text":"\"Statement 1\"","textRange":"8:14::27","type":"STRING_LITERAL"}, + {"text":")","textRange":"8:27::28"}, + {"text":"}","textRange":"9:0::1"}, + {"text":"func","textRange":"10:0::4","type":"KEYWORD"}, + {"text":"fun2","textRange":"10:5::9"}, + {"text":"(","textRange":"10:9::10"}, + {"text":"i","textRange":"10:10::11"}, + {"text":"int","textRange":"10:12::15"}, + {"text":")","textRange":"10:15::16"}, + {"text":"{","textRange":"10:17::18"}, + {"text":"switch","textRange":"11:2::8","type":"KEYWORD"}, + {"text":"i","textRange":"11:9::10"}, + {"text":"{","textRange":"11:11::12"}, + {"text":"case","textRange":"12:2::6","type":"KEYWORD"}, + {"text":"2","textRange":"12:7::8"}, + {"text":":","textRange":"12:8::9"}, + {"text":"fmt","textRange":"13:4::7"}, + {"text":"Println","textRange":"13:8::15"}, + {"text":".","textRange":"13:7::8"}, + {"text":"(","textRange":"13:15::16"}, + {"text":"\"Statement 3\"","textRange":"14:6::19","type":"STRING_LITERAL"}, + {"text":")","textRange":"15:4::5"}, + {"text":",","textRange":"14:19::20"}, + {"text":"}","textRange":"16:2::3"}, + {"text":"}","textRange":"17:0::1"}, + {"text":"func","textRange":"18:0::4","type":"KEYWORD"}, + {"text":"fun3","textRange":"18:5::9"}, + {"text":"(","textRange":"18:9::10"}, + {"text":"x","textRange":"18:10::11"}, + {"text":"interface","textRange":"18:12::21","type":"KEYWORD"}, + {"text":"{","textRange":"18:21::22"}, + {"text":"}","textRange":"18:22::23"}, + {"text":")","textRange":"18:23::24"}, + {"text":"int","textRange":"18:25::28"}, + {"text":"{","textRange":"18:29::30"}, + {"text":"return","textRange":"19:2::8","type":"KEYWORD"}, + {"text":"42","textRange":"19:9::11"}, + {"text":"}","textRange":"20:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:20:1","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0:2:12","children":[ + {"@type": "Native", "metaData": "2:0::7"}, + {"@type": "Identifier", "metaData": "2:8::12","name":"main"} + ]}, + {"@type": "ImportDeclaration", "metaData": "3:0::12","children":[ + {"@type": "Native", "metaData": "3:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "3:7::12","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "3:7::12","value":"\"fmt\"","content":"fmt"} + ]} + ]}, + {"@type": "ClassDeclaration", "metaData": "4:0::31","identifier":"4:5::11","classTree": + {"@type": "Native", "metaData": "4:0::31","nativeKind":"[1](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "4:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "4:5::11","name":"class1"}, + {"@type": "Native", "metaData": "4:12::31","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "4:12::18","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "4:19::31","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "4:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "4:21::29","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "4:21::25","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "4:21::22","name":"x"}, + {"@type": "Native", "metaData": "4:22::23"}, + {"@type": "Identifier", "metaData": "4:24::25","name":"y"} + ]}, + {"@type": "Identifier", "metaData": "4:26::29","name":"int"} + ]}, + {"@type": "Native", "metaData": "4:30::31","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "ClassDeclaration", "metaData": "5:0::34","identifier":"5:5::11","classTree": + {"@type": "Native", "metaData": "5:0::34","nativeKind":"[2](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "5:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "5:5::11","name":"class2"}, + {"@type": "Native", "metaData": "5:12::34","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "5:12::18","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "5:19::34","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "5:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "5:21::32","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "5:21::25","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "5:21::22","name":"a"}, + {"@type": "Native", "metaData": "5:22::23"}, + {"@type": "Identifier", "metaData": "5:24::25","name":"b"} + ]}, + {"@type": "Identifier", "metaData": "5:26::32","name":"string"} + ]}, + {"@type": "Native", "metaData": "5:33::34","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "ClassDeclaration", "metaData": "6:0::27","identifier":"6:5::14","classTree": + {"@type": "Native", "metaData": "6:0::27","nativeKind":"[3](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "6:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "6:5::14","name":"anyObject"}, + {"@type": "Native", "metaData": "6:15::27","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "6:15::24","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "6:25::27","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "6:25::26","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "6:26::27","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "7:0:9:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "7:5::9","name":"fun1"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "7:12:9:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "8:2::28","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "8:2::28","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "8:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "8:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "8:5::6"}, + {"@type": "Identifier", "metaData": "8:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "8:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "8:14::27","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "8:14::27","value":"\"Statement 1\"","content":"Statement 1"} + ]}, + {"@type": "Native", "metaData": "8:27::28","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "10:0:17:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "10:5::9","name":"fun2"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "10:10::15","type": + {"@type": "Identifier", "metaData": "10:12::15","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "10:10::11","name":"i"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "10:17:17:1","statementOrExpressions":[ + {"@type": "Match", "metaData": "11:2:16:3","keyword":"11:2::8","expression": + {"@type": "Native", "metaData": "11:9::10","nativeKind":"InitAndTag","children":[ + {"@type": "Identifier", "metaData": "11:9::10","name":"i"} + ]},"cases":[ + {"@type": "MatchCase", "metaData": "12:2:15:5","expression": + {"@type": "Native", "metaData": "12:7::8","nativeKind":"CaseExprList","children":[ + {"@type": "IntegerLiteral", "metaData": "12:7::8","value":"2"} + ]},"body": + {"@type": "Block", "metaData": "13:4:15:5","statementOrExpressions":[ + {"@type": "Native", "metaData": "13:4:15:5","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "13:4:15:5","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "13:4::15","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "13:4::7","name":"fmt"}, + {"@type": "Native", "metaData": "13:7::8"}, + {"@type": "Identifier", "metaData": "13:8::15","name":"Println"} + ]}, + {"@type": "Native", "metaData": "13:15::16","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "14:6::19","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "14:6::19","value":"\"Statement 3\"","content":"Statement 3"} + ]}, + {"@type": "Native", "metaData": "14:19::20"}, + {"@type": "Native", "metaData": "15:4::5","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "18:0:20:1","returnType": + {"@type": "Native", "metaData": "18:25::28","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "18:25::28","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "18:25::28","name":"int"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "18:5::9","name":"fun3"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "18:10::23","type": + {"@type": "Native", "metaData": "18:12::23","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "18:12::21","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "18:21::23","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "18:21::22","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "18:22::23","nativeKind":"Closing"} + ]} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "18:10::11","name":"x"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "18:29:20:1","statementOrExpressions":[ + {"@type": "Return", "metaData": "19:2::11","keyword":"19:2::8","body": + {"@type": "IntegerLiteral", "metaData": "19:9::11","value":"42"}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/literal.go.source b/sonar-go-to-slang/resources/ast/literal.go.source new file mode 100644 index 00000000..f7c22fa9 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/literal.go.source @@ -0,0 +1,12 @@ +package ast + +func allLiterals() { + stringLit := "hello, world" + multiLineStringLit := `hello world` + intLit := 42 + hexLit := 0xFF42 + octalLit := 042 + floatLit := 4.2 + charLit := 'c' + imagLit := 123.45i //mapped to native node +} diff --git a/sonar-go-to-slang/resources/ast/literal.json b/sonar-go-to-slang/resources/ast/literal.json new file mode 100644 index 00000000..4cfe1fbf --- /dev/null +++ b/sonar-go-to-slang/resources/ast/literal.json @@ -0,0 +1,77 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"//mapped to native node", "contentText":"mapped to native node", "range":"11:20::43", "contentRange": "11:22::43"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"}, + {"text":"func","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"allLiterals","textRange":"3:5::16"}, + {"text":"(","textRange":"3:16::17"}, + {"text":")","textRange":"3:17::18"}, + {"text":"{","textRange":"3:19::20"}, + {"text":"stringLit","textRange":"4:1::10"}, + {"text":":=","textRange":"4:11::13"}, + {"text":"\"hello, world\"","textRange":"4:14::28","type":"STRING_LITERAL"}, + {"text":"multiLineStringLit","textRange":"5:1::19"}, + {"text":":=","textRange":"5:20::22"}, + {"text":"`hello world`","textRange":"5:23::36"}, + {"text":"intLit","textRange":"6:1::7"}, + {"text":":=","textRange":"6:8::10"}, + {"text":"42","textRange":"6:11::13"}, + {"text":"hexLit","textRange":"7:1::7"}, + {"text":":=","textRange":"7:8::10"}, + {"text":"0xFF42","textRange":"7:11::17"}, + {"text":"octalLit","textRange":"8:1::9"}, + {"text":":=","textRange":"8:10::12"}, + {"text":"042","textRange":"8:13::16"}, + {"text":"floatLit","textRange":"9:1::9"}, + {"text":":=","textRange":"9:10::12"}, + {"text":"4.2","textRange":"9:13::16"}, + {"text":"charLit","textRange":"10:1::8"}, + {"text":":=","textRange":"10:9::11"}, + {"text":"'c'","textRange":"10:12::15"}, + {"text":"imagLit","textRange":"11:1::8"}, + {"text":":=","textRange":"11:9::11"}, + {"text":"123.45i","textRange":"11:12::19"}, + {"text":"}","textRange":"12:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:13:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "3:0:12:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "3:5::16","name":"allLiterals"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "3:19:12:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "4:1::28","type":null,"isVal":false,"initializer": + {"@type": "StringLiteral", "metaData": "4:14::28","value":"\"hello, world\"","content":"hello, world"},"identifier": + {"@type": "Identifier", "metaData": "4:1::10","name":"stringLit"}}, + {"@type": "VariableDeclaration", "metaData": "5:1::36","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "5:23::36","nativeKind":"[0](BasicLit)"},"identifier": + {"@type": "Identifier", "metaData": "5:1::19","name":"multiLineStringLit"}}, + {"@type": "VariableDeclaration", "metaData": "6:1::13","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "6:11::13","value":"42"},"identifier": + {"@type": "Identifier", "metaData": "6:1::7","name":"intLit"}}, + {"@type": "VariableDeclaration", "metaData": "7:1::17","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "7:11::17","value":"0xFF42"},"identifier": + {"@type": "Identifier", "metaData": "7:1::7","name":"hexLit"}}, + {"@type": "VariableDeclaration", "metaData": "8:1::16","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "8:13::16","value":"042"},"identifier": + {"@type": "Identifier", "metaData": "8:1::9","name":"octalLit"}}, + {"@type": "VariableDeclaration", "metaData": "9:1::16","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "9:13::16","nativeKind":"[0](BasicLit)"},"identifier": + {"@type": "Identifier", "metaData": "9:1::9","name":"floatLit"}}, + {"@type": "VariableDeclaration", "metaData": "10:1::15","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "10:12::15","nativeKind":"[0](BasicLit)"},"identifier": + {"@type": "Identifier", "metaData": "10:1::8","name":"charLit"}}, + {"@type": "VariableDeclaration", "metaData": "11:1::19","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "11:12::19","nativeKind":"[0](BasicLit)"},"identifier": + {"@type": "Identifier", "metaData": "11:1::8","name":"imagLit"}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/loops.go.source b/sonar-go-to-slang/resources/ast/loops.go.source new file mode 100644 index 00000000..df66b726 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/loops.go.source @@ -0,0 +1,46 @@ +package ast + +func foo() { + for i := 0; i < 10; i++ { + } + + //For loop equivalent to while loop + n := 1 + for n < 5 { + n *= 2 + } + + //Infinite loop + sum := 0 + for { + sum++ // repeated forever + } + fmt.Println(sum) + + //Range loop, equivalent to "for-each" + strings := []string{"hello", "world"} + for i, s := range strings { + fmt.Println(i, s) + } + + //Continue and break behave as in Java + sum := 0 +OUTER: + for i := 1; i < 500; i++ { + if i%2 != 0 { // skip odd numbers + continue + } + if i == 100 { // stop at 100 + break + } + if i%2 != 0 { // skip odd numbers + continue OUTER + } + sum += i + } + + //multiple initialization; a consolidated bool expression with && and ||; multiple incrementation + for i, j, s := 0, 5, "a"; i < 3 && j < 100 && s != "aaaaa"; i, j, s = i+1, j+1, s+"a" { + fmt.Println("Value of i, j, s:", i, j, s) + } +} diff --git a/sonar-go-to-slang/resources/ast/loops.json b/sonar-go-to-slang/resources/ast/loops.json new file mode 100644 index 00000000..c5b7ee7a --- /dev/null +++ b/sonar-go-to-slang/resources/ast/loops.json @@ -0,0 +1,440 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"//For loop equivalent to while loop", "contentText":"For loop equivalent to while loop", "range":"7:1::36", "contentRange": "7:3::36"}, + {"text":"//Infinite loop", "contentText":"Infinite loop", "range":"13:1::16", "contentRange": "13:3::16"}, + {"text":"// repeated forever", "contentText":" repeated forever", "range":"16:8::27", "contentRange": "16:10::27"}, + {"text":"//Range loop, equivalent to \"for-each\"", "contentText":"Range loop, equivalent to \"for-each\"", "range":"20:1::39", "contentRange": "20:3::39"}, + {"text":"//Continue and break behave as in Java", "contentText":"Continue and break behave as in Java", "range":"26:1::39", "contentRange": "26:3::39"}, + {"text":"// skip odd numbers", "contentText":" skip odd numbers", "range":"30:16::35", "contentRange": "30:18::35"}, + {"text":"// stop at 100", "contentText":" stop at 100", "range":"33:16::30", "contentRange": "33:18::30"}, + {"text":"// skip odd numbers", "contentText":" skip odd numbers", "range":"36:16::35", "contentRange": "36:18::35"}, + {"text":"//multiple initialization; a consolidated bool expression with \u0026\u0026 and ||; multiple incrementation", "contentText":"multiple initialization; a consolidated bool expression with \u0026\u0026 and ||; multiple incrementation", "range":"42:1::98", "contentRange": "42:3::98"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"}, + {"text":"func","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"foo","textRange":"3:5::8"}, + {"text":"(","textRange":"3:8::9"}, + {"text":")","textRange":"3:9::10"}, + {"text":"{","textRange":"3:11::12"}, + {"text":"for","textRange":"4:1::4","type":"KEYWORD"}, + {"text":"i","textRange":"4:5::6"}, + {"text":":=","textRange":"4:7::9"}, + {"text":"0","textRange":"4:10::11"}, + {"text":"i","textRange":"4:13::14"}, + {"text":"\u003c","textRange":"4:15::16"}, + {"text":"10","textRange":"4:17::19"}, + {"text":";","textRange":"4:11::12"}, + {"text":"i","textRange":"4:21::22"}, + {"text":"++","textRange":"4:22::24"}, + {"text":";","textRange":"4:19::20"}, + {"text":"{","textRange":"4:25::26"}, + {"text":"}","textRange":"5:1::2"}, + {"text":"n","textRange":"8:1::2"}, + {"text":":=","textRange":"8:3::5"}, + {"text":"1","textRange":"8:6::7"}, + {"text":"for","textRange":"9:1::4","type":"KEYWORD"}, + {"text":"n","textRange":"9:5::6"}, + {"text":"\u003c","textRange":"9:7::8"}, + {"text":"5","textRange":"9:9::10"}, + {"text":"{","textRange":"9:11::12"}, + {"text":"n","textRange":"10:2::3"}, + {"text":"*=","textRange":"10:4::6"}, + {"text":"2","textRange":"10:7::8"}, + {"text":"}","textRange":"11:1::2"}, + {"text":"sum","textRange":"14:1::4"}, + {"text":":=","textRange":"14:5::7"}, + {"text":"0","textRange":"14:8::9"}, + {"text":"for","textRange":"15:1::4","type":"KEYWORD"}, + {"text":"{","textRange":"15:5::6"}, + {"text":"sum","textRange":"16:2::5"}, + {"text":"++","textRange":"16:5::7"}, + {"text":"}","textRange":"17:1::2"}, + {"text":"fmt","textRange":"18:1::4"}, + {"text":"Println","textRange":"18:5::12"}, + {"text":".","textRange":"18:4::5"}, + {"text":"(","textRange":"18:12::13"}, + {"text":"sum","textRange":"18:13::16"}, + {"text":")","textRange":"18:16::17"}, + {"text":"strings","textRange":"21:1::8"}, + {"text":":=","textRange":"21:9::11"}, + {"text":"[","textRange":"21:12::13"}, + {"text":"string","textRange":"21:14::20"}, + {"text":"]","textRange":"21:13::14"}, + {"text":"{","textRange":"21:20::21"}, + {"text":"\"hello\"","textRange":"21:21::28","type":"STRING_LITERAL"}, + {"text":"\"world\"","textRange":"21:30::37","type":"STRING_LITERAL"}, + {"text":",","textRange":"21:28::29"}, + {"text":"}","textRange":"21:37::38"}, + {"text":"for","textRange":"22:1::4","type":"KEYWORD"}, + {"text":"i","textRange":"22:5::6"}, + {"text":"s","textRange":"22:8::9"}, + {"text":",","textRange":"22:6::7"}, + {"text":":=","textRange":"22:10::12"}, + {"text":"strings","textRange":"22:19::26"}, + {"text":"range","textRange":"22:13::18","type":"KEYWORD"}, + {"text":"{","textRange":"22:27::28"}, + {"text":"fmt","textRange":"23:2::5"}, + {"text":"Println","textRange":"23:6::13"}, + {"text":".","textRange":"23:5::6"}, + {"text":"(","textRange":"23:13::14"}, + {"text":"i","textRange":"23:14::15"}, + {"text":"s","textRange":"23:17::18"}, + {"text":",","textRange":"23:15::16"}, + {"text":")","textRange":"23:18::19"}, + {"text":"}","textRange":"24:1::2"}, + {"text":"sum","textRange":"27:1::4"}, + {"text":":=","textRange":"27:5::7"}, + {"text":"0","textRange":"27:8::9"}, + {"text":"OUTER","textRange":"28:0::5"}, + {"text":":","textRange":"28:5::6"}, + {"text":"for","textRange":"29:1::4","type":"KEYWORD"}, + {"text":"i","textRange":"29:5::6"}, + {"text":":=","textRange":"29:7::9"}, + {"text":"1","textRange":"29:10::11"}, + {"text":"i","textRange":"29:13::14"}, + {"text":"\u003c","textRange":"29:15::16"}, + {"text":"500","textRange":"29:17::20"}, + {"text":";","textRange":"29:11::12"}, + {"text":"i","textRange":"29:22::23"}, + {"text":"++","textRange":"29:23::25"}, + {"text":";","textRange":"29:20::21"}, + {"text":"{","textRange":"29:26::27"}, + {"text":"if","textRange":"30:2::4","type":"KEYWORD"}, + {"text":"i","textRange":"30:5::6"}, + {"text":"%","textRange":"30:6::7"}, + {"text":"2","textRange":"30:7::8"}, + {"text":"!=","textRange":"30:9::11"}, + {"text":"0","textRange":"30:12::13"}, + {"text":"{","textRange":"30:14::15"}, + {"text":"continue","textRange":"31:3::11","type":"KEYWORD"}, + {"text":"}","textRange":"32:2::3"}, + {"text":"if","textRange":"33:2::4","type":"KEYWORD"}, + {"text":"i","textRange":"33:5::6"}, + {"text":"==","textRange":"33:7::9"}, + {"text":"100","textRange":"33:10::13"}, + {"text":"{","textRange":"33:14::15"}, + {"text":"break","textRange":"34:3::8","type":"KEYWORD"}, + {"text":"}","textRange":"35:2::3"}, + {"text":"if","textRange":"36:2::4","type":"KEYWORD"}, + {"text":"i","textRange":"36:5::6"}, + {"text":"%","textRange":"36:6::7"}, + {"text":"2","textRange":"36:7::8"}, + {"text":"!=","textRange":"36:9::11"}, + {"text":"0","textRange":"36:12::13"}, + {"text":"{","textRange":"36:14::15"}, + {"text":"continue","textRange":"37:3::11","type":"KEYWORD"}, + {"text":"OUTER","textRange":"37:12::17"}, + {"text":"}","textRange":"38:2::3"}, + {"text":"sum","textRange":"39:2::5"}, + {"text":"+=","textRange":"39:6::8"}, + {"text":"i","textRange":"39:9::10"}, + {"text":"}","textRange":"40:1::2"}, + {"text":"for","textRange":"43:1::4","type":"KEYWORD"}, + {"text":"i","textRange":"43:5::6"}, + {"text":"j","textRange":"43:8::9"}, + {"text":",","textRange":"43:6::7"}, + {"text":"s","textRange":"43:11::12"}, + {"text":",","textRange":"43:9::10"}, + {"text":":=","textRange":"43:13::15"}, + {"text":"0","textRange":"43:16::17"}, + {"text":"5","textRange":"43:19::20"}, + {"text":",","textRange":"43:17::18"}, + {"text":"\"a\"","textRange":"43:22::25","type":"STRING_LITERAL"}, + {"text":",","textRange":"43:20::21"}, + {"text":"i","textRange":"43:27::28"}, + {"text":"\u003c","textRange":"43:29::30"}, + {"text":"3","textRange":"43:31::32"}, + {"text":"\u0026\u0026","textRange":"43:33::35"}, + {"text":"j","textRange":"43:36::37"}, + {"text":"\u003c","textRange":"43:38::39"}, + {"text":"100","textRange":"43:40::43"}, + {"text":"\u0026\u0026","textRange":"43:44::46"}, + {"text":"s","textRange":"43:47::48"}, + {"text":"!=","textRange":"43:49::51"}, + {"text":"\"aaaaa\"","textRange":"43:52::59","type":"STRING_LITERAL"}, + {"text":";","textRange":"43:25::26"}, + {"text":"i","textRange":"43:61::62"}, + {"text":"j","textRange":"43:64::65"}, + {"text":",","textRange":"43:62::63"}, + {"text":"s","textRange":"43:67::68"}, + {"text":",","textRange":"43:65::66"}, + {"text":"=","textRange":"43:69::70"}, + {"text":"i","textRange":"43:71::72"}, + {"text":"+","textRange":"43:72::73"}, + {"text":"1","textRange":"43:73::74"}, + {"text":"j","textRange":"43:76::77"}, + {"text":"+","textRange":"43:77::78"}, + {"text":"1","textRange":"43:78::79"}, + {"text":",","textRange":"43:74::75"}, + {"text":"s","textRange":"43:81::82"}, + {"text":"+","textRange":"43:82::83"}, + {"text":"\"a\"","textRange":"43:83::86","type":"STRING_LITERAL"}, + {"text":",","textRange":"43:79::80"}, + {"text":";","textRange":"43:59::60"}, + {"text":"{","textRange":"43:87::88"}, + {"text":"fmt","textRange":"44:2::5"}, + {"text":"Println","textRange":"44:6::13"}, + {"text":".","textRange":"44:5::6"}, + {"text":"(","textRange":"44:13::14"}, + {"text":"\"Value of i, j, s:\"","textRange":"44:14::33","type":"STRING_LITERAL"}, + {"text":"i","textRange":"44:35::36"}, + {"text":",","textRange":"44:33::34"}, + {"text":"j","textRange":"44:38::39"}, + {"text":",","textRange":"44:36::37"}, + {"text":"s","textRange":"44:41::42"}, + {"text":",","textRange":"44:39::40"}, + {"text":")","textRange":"44:42::43"}, + {"text":"}","textRange":"45:1::2"}, + {"text":"}","textRange":"46:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:47:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "3:0:46:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "3:5::8","name":"foo"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "3:11:46:1","statementOrExpressions":[ + {"@type": "Loop", "metaData": "4:1:5:2","kind":"FOR","keyword":"4:1::4","condition": + {"@type": "Native", "metaData": "4:5::24","nativeKind":"ForHeader","children":[ + {"@type": "VariableDeclaration", "metaData": "4:5::11","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "4:10::11","value":"0"},"identifier": + {"@type": "Identifier", "metaData": "4:5::6","name":"i"}}, + {"@type": "Native", "metaData": "4:11::12","nativeKind":"Semicolon"}, + {"@type": "BinaryExpression", "metaData": "4:13::19","rightOperand": + {"@type": "IntegerLiteral", "metaData": "4:17::19","value":"10"},"operatorToken":"4:15::16","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "4:13::14","name":"i"}}, + {"@type": "Native", "metaData": "4:19::20","nativeKind":"Semicolon"}, + {"@type": "UnaryExpression", "metaData": "4:21::24","operator":"INCREMENT","operand": + {"@type": "Identifier", "metaData": "4:21::22","name":"i"}} + ]},"body": + {"@type": "Block", "metaData": "4:25:5:2","statementOrExpressions":[]}}, + {"@type": "VariableDeclaration", "metaData": "8:1::7","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "8:6::7","value":"1"},"identifier": + {"@type": "Identifier", "metaData": "8:1::2","name":"n"}}, + {"@type": "Loop", "metaData": "9:1:11:2","kind":"WHILE","keyword":"9:1::4","condition": + {"@type": "BinaryExpression", "metaData": "9:5::10","rightOperand": + {"@type": "IntegerLiteral", "metaData": "9:9::10","value":"5"},"operatorToken":"9:7::8","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "9:5::6","name":"n"}},"body": + {"@type": "Block", "metaData": "9:11:11:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "10:2::8","nativeKind":"[0](AssignStmt)","children":[ + {"@type": "Native", "metaData": "10:2::3","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "10:2::3","name":"n"} + ]}, + {"@type": "Native", "metaData": "10:4::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "10:7::8","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "10:7::8","value":"2"} + ]} + ]} + ]}}, + {"@type": "VariableDeclaration", "metaData": "14:1::9","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "14:8::9","value":"0"},"identifier": + {"@type": "Identifier", "metaData": "14:1::4","name":"sum"}}, + {"@type": "Loop", "metaData": "15:1:17:2","kind":"WHILE","keyword":"15:1::4","condition": + null,"body": + {"@type": "Block", "metaData": "15:5:17:2","statementOrExpressions":[ + {"@type": "UnaryExpression", "metaData": "16:2::7","operator":"INCREMENT","operand": + {"@type": "Identifier", "metaData": "16:2::5","name":"sum"}} + ]}}, + {"@type": "Native", "metaData": "18:1::17","nativeKind":"[5](ExprStmt)","children":[ + {"@type": "Native", "metaData": "18:1::17","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "18:1::12","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "18:1::4","name":"fmt"}, + {"@type": "Native", "metaData": "18:4::5"}, + {"@type": "Identifier", "metaData": "18:5::12","name":"Println"} + ]}, + {"@type": "Native", "metaData": "18:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "18:13::16","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "18:13::16","name":"sum"} + ]}, + {"@type": "Native", "metaData": "18:16::17","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "VariableDeclaration", "metaData": "21:1::38","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "21:12::38","nativeKind":"[0](CompositeLit)","children":[ + {"@type": "Native", "metaData": "21:12::20","nativeKind":"Type(ArrayType)","children":[ + {"@type": "Native", "metaData": "21:12::13","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "21:13::14"}, + {"@type": "Identifier", "metaData": "21:14::20","name":"string"} + ]}, + {"@type": "Native", "metaData": "21:20::21","nativeKind":"Lbrace"}, + {"@type": "Native", "metaData": "21:21::37","nativeKind":"Elts([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "21:21::28","value":"\"hello\"","content":"hello"}, + {"@type": "Native", "metaData": "21:28::29"}, + {"@type": "StringLiteral", "metaData": "21:30::37","value":"\"world\"","content":"world"} + ]}, + {"@type": "Native", "metaData": "21:37::38","nativeKind":"Rbrace"} + ]},"identifier": + {"@type": "Identifier", "metaData": "21:1::8","name":"strings"}}, + {"@type": "Loop", "metaData": "22:1:24:2","kind":"FOR","keyword":"22:1::4","condition": + {"@type": "Native", "metaData": "22:5::26","nativeKind":"RangeHeader","children":[ + {"@type": "Identifier", "metaData": "22:5::6","name":"i"}, + {"@type": "Native", "metaData": "22:6::7"}, + {"@type": "Identifier", "metaData": "22:8::9","name":"s"}, + {"@type": "Native", "metaData": "22:10::12","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "22:13::18"}, + {"@type": "Identifier", "metaData": "22:19::26","name":"strings"} + ]},"body": + {"@type": "Block", "metaData": "22:27:24:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "23:2::19","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "23:2::19","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "23:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "23:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "23:5::6"}, + {"@type": "Identifier", "metaData": "23:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "23:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "23:14::18","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "23:14::15","name":"i"}, + {"@type": "Native", "metaData": "23:15::16"}, + {"@type": "Identifier", "metaData": "23:17::18","name":"s"} + ]}, + {"@type": "Native", "metaData": "23:18::19","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "VariableDeclaration", "metaData": "27:1::9","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "27:8::9","value":"0"},"identifier": + {"@type": "Identifier", "metaData": "27:1::4","name":"sum"}}, + {"@type": "Native", "metaData": "28:0:40:2","nativeKind":"[9](LabeledStmt)","children":[ + {"@type": "Identifier", "metaData": "28:0::5","name":"OUTER"}, + {"@type": "Native", "metaData": "28:5::6","nativeKind":"Colon"}, + {"@type": "Loop", "metaData": "29:1:40:2","kind":"FOR","keyword":"29:1::4","condition": + {"@type": "Native", "metaData": "29:5::25","nativeKind":"ForHeader","children":[ + {"@type": "VariableDeclaration", "metaData": "29:5::11","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "29:10::11","value":"1"},"identifier": + {"@type": "Identifier", "metaData": "29:5::6","name":"i"}}, + {"@type": "Native", "metaData": "29:11::12","nativeKind":"Semicolon"}, + {"@type": "BinaryExpression", "metaData": "29:13::20","rightOperand": + {"@type": "IntegerLiteral", "metaData": "29:17::20","value":"500"},"operatorToken":"29:15::16","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "29:13::14","name":"i"}}, + {"@type": "Native", "metaData": "29:20::21","nativeKind":"Semicolon"}, + {"@type": "UnaryExpression", "metaData": "29:22::25","operator":"INCREMENT","operand": + {"@type": "Identifier", "metaData": "29:22::23","name":"i"}} + ]},"body": + {"@type": "Block", "metaData": "29:26:40:2","statementOrExpressions":[ + {"@type": "If", "metaData": "30:2:32:3","thenBranch": + {"@type": "Block", "metaData": "30:14:32:3","statementOrExpressions":[ + {"@type": "Jump", "metaData": "31:3::11","label": + null,"kind":"CONTINUE","keyword":"31:3::11"} + ]},"ifKeyword":"30:2::4","elseKeyword":null,"elseBranch": + null,"condition": + {"@type": "BinaryExpression", "metaData": "30:5::13","rightOperand": + {"@type": "IntegerLiteral", "metaData": "30:12::13","value":"0"},"operatorToken":"30:9::11","operator":"NOT_EQUAL_TO","leftOperand": + {"@type": "Native", "metaData": "30:5::8","nativeKind":"operand(BinaryExpr)","children":[ + {"@type": "Identifier", "metaData": "30:5::6","name":"i"}, + {"@type": "Native", "metaData": "30:6::7","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "30:7::8","value":"2"} + ]}}}, + {"@type": "If", "metaData": "33:2:35:3","thenBranch": + {"@type": "Block", "metaData": "33:14:35:3","statementOrExpressions":[ + {"@type": "Jump", "metaData": "34:3::8","label": + null,"kind":"BREAK","keyword":"34:3::8"} + ]},"ifKeyword":"33:2::4","elseKeyword":null,"elseBranch": + null,"condition": + {"@type": "BinaryExpression", "metaData": "33:5::13","rightOperand": + {"@type": "IntegerLiteral", "metaData": "33:10::13","value":"100"},"operatorToken":"33:7::9","operator":"EQUAL_TO","leftOperand": + {"@type": "Identifier", "metaData": "33:5::6","name":"i"}}}, + {"@type": "If", "metaData": "36:2:38:3","thenBranch": + {"@type": "Block", "metaData": "36:14:38:3","statementOrExpressions":[ + {"@type": "Jump", "metaData": "37:3::17","label": + {"@type": "Identifier", "metaData": "37:12::17","name":"OUTER"},"kind":"CONTINUE","keyword":"37:3::11"} + ]},"ifKeyword":"36:2::4","elseKeyword":null,"elseBranch": + null,"condition": + {"@type": "BinaryExpression", "metaData": "36:5::13","rightOperand": + {"@type": "IntegerLiteral", "metaData": "36:12::13","value":"0"},"operatorToken":"36:9::11","operator":"NOT_EQUAL_TO","leftOperand": + {"@type": "Native", "metaData": "36:5::8","nativeKind":"operand(BinaryExpr)","children":[ + {"@type": "Identifier", "metaData": "36:5::6","name":"i"}, + {"@type": "Native", "metaData": "36:6::7","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "36:7::8","value":"2"} + ]}}}, + {"@type": "AssignmentExpression", "metaData": "39:2::10","statementOrExpression": + {"@type": "Identifier", "metaData": "39:9::10","name":"i"},"operator":"PLUS_EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "39:2::5","name":"sum"}} + ]}} + ]}, + {"@type": "Loop", "metaData": "43:1:45:2","kind":"FOR","keyword":"43:1::4","condition": + {"@type": "Native", "metaData": "43:5::86","nativeKind":"ForHeader","children":[ + {"@type": "Native", "metaData": "43:5::25","nativeKind":"Init(AssignStmt)","children":[ + {"@type": "Native", "metaData": "43:5::12","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "43:5::6","name":"i"}, + {"@type": "Native", "metaData": "43:6::7"}, + {"@type": "Identifier", "metaData": "43:8::9","name":"j"}, + {"@type": "Native", "metaData": "43:9::10"}, + {"@type": "Identifier", "metaData": "43:11::12","name":"s"} + ]}, + {"@type": "Native", "metaData": "43:13::15","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "43:16::25","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "43:16::17","value":"0"}, + {"@type": "Native", "metaData": "43:17::18"}, + {"@type": "IntegerLiteral", "metaData": "43:19::20","value":"5"}, + {"@type": "Native", "metaData": "43:20::21"}, + {"@type": "StringLiteral", "metaData": "43:22::25","value":"\"a\"","content":"a"} + ]} + ]}, + {"@type": "Native", "metaData": "43:25::26","nativeKind":"Semicolon"}, + {"@type": "BinaryExpression", "metaData": "43:27::59","rightOperand": + {"@type": "BinaryExpression", "metaData": "43:47::59","rightOperand": + {"@type": "StringLiteral", "metaData": "43:52::59","value":"\"aaaaa\"","content":"aaaaa"},"operatorToken":"43:49::51","operator":"NOT_EQUAL_TO","leftOperand": + {"@type": "Identifier", "metaData": "43:47::48","name":"s"}},"operatorToken":"43:44::46","operator":"CONDITIONAL_AND","leftOperand": + {"@type": "BinaryExpression", "metaData": "43:27::43","rightOperand": + {"@type": "BinaryExpression", "metaData": "43:36::43","rightOperand": + {"@type": "IntegerLiteral", "metaData": "43:40::43","value":"100"},"operatorToken":"43:38::39","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "43:36::37","name":"j"}},"operatorToken":"43:33::35","operator":"CONDITIONAL_AND","leftOperand": + {"@type": "BinaryExpression", "metaData": "43:27::32","rightOperand": + {"@type": "IntegerLiteral", "metaData": "43:31::32","value":"3"},"operatorToken":"43:29::30","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "43:27::28","name":"i"}}}}, + {"@type": "Native", "metaData": "43:59::60","nativeKind":"Semicolon"}, + {"@type": "AssignmentExpression", "metaData": "43:61::86","statementOrExpression": + {"@type": "Native", "metaData": "43:71::86","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "BinaryExpression", "metaData": "43:71::74","rightOperand": + {"@type": "IntegerLiteral", "metaData": "43:73::74","value":"1"},"operatorToken":"43:72::73","operator":"PLUS","leftOperand": + {"@type": "Identifier", "metaData": "43:71::72","name":"i"}}, + {"@type": "Native", "metaData": "43:74::75"}, + {"@type": "BinaryExpression", "metaData": "43:76::79","rightOperand": + {"@type": "IntegerLiteral", "metaData": "43:78::79","value":"1"},"operatorToken":"43:77::78","operator":"PLUS","leftOperand": + {"@type": "Identifier", "metaData": "43:76::77","name":"j"}}, + {"@type": "Native", "metaData": "43:79::80"}, + {"@type": "BinaryExpression", "metaData": "43:81::86","rightOperand": + {"@type": "StringLiteral", "metaData": "43:83::86","value":"\"a\"","content":"a"},"operatorToken":"43:82::83","operator":"PLUS","leftOperand": + {"@type": "Identifier", "metaData": "43:81::82","name":"s"}} + ]},"operator":"EQUAL","leftHandSide": + {"@type": "Native", "metaData": "43:61::68","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "43:61::62","name":"i"}, + {"@type": "Native", "metaData": "43:62::63"}, + {"@type": "Identifier", "metaData": "43:64::65","name":"j"}, + {"@type": "Native", "metaData": "43:65::66"}, + {"@type": "Identifier", "metaData": "43:67::68","name":"s"} + ]}} + ]},"body": + {"@type": "Block", "metaData": "43:87:45:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "44:2::43","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "44:2::43","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "44:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "44:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "44:5::6"}, + {"@type": "Identifier", "metaData": "44:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "44:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "44:14::42","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "44:14::33","value":"\"Value of i, j, s:\"","content":"Value of i, j, s:"}, + {"@type": "Native", "metaData": "44:33::34"}, + {"@type": "Identifier", "metaData": "44:35::36","name":"i"}, + {"@type": "Native", "metaData": "44:36::37"}, + {"@type": "Identifier", "metaData": "44:38::39","name":"j"}, + {"@type": "Native", "metaData": "44:39::40"}, + {"@type": "Identifier", "metaData": "44:41::42","name":"s"} + ]}, + {"@type": "Native", "metaData": "44:42::43","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/method_receiver.go.source b/sonar-go-to-slang/resources/ast/method_receiver.go.source new file mode 100644 index 00000000..e826b540 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/method_receiver.go.source @@ -0,0 +1,3 @@ +package main + +func (v Vertex) foo(p1 int) {} diff --git a/sonar-go-to-slang/resources/ast/method_receiver.json b/sonar-go-to-slang/resources/ast/method_receiver.json new file mode 100644 index 00000000..08e55aeb --- /dev/null +++ b/sonar-go-to-slang/resources/ast/method_receiver.json @@ -0,0 +1,48 @@ +{ + "treeMetaData": { + "comments": [ + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"func","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"(","textRange":"3:5::6"}, + {"text":"v","textRange":"3:6::7"}, + {"text":"Vertex","textRange":"3:8::14"}, + {"text":")","textRange":"3:14::15"}, + {"text":"foo","textRange":"3:16::19"}, + {"text":"(","textRange":"3:19::20"}, + {"text":"p1","textRange":"3:20::22"}, + {"text":"int","textRange":"3:23::26"}, + {"text":")","textRange":"3:26::27"}, + {"text":"{","textRange":"3:28::29"}, + {"text":"}","textRange":"3:29::30"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:4:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "3:0::30","returnType": + null,"nativeChildren":[ + {"@type": "Native", "metaData": "3:5::15","nativeKind":"Recv(FieldList)","children":[ + {"@type": "Native", "metaData": "3:5::6","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "3:6::14","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "3:6::7","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "3:6::7","name":"v"} + ]}, + {"@type": "Identifier", "metaData": "3:8::14","name":"Vertex"} + ]}, + {"@type": "Native", "metaData": "3:14::15","nativeKind":"Closing"} + ]} + ],"name": + {"@type": "Identifier", "metaData": "3:16::19","name":"foo"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "3:20::26","type": + {"@type": "Identifier", "metaData": "3:23::26","name":"int"},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "3:20::22","name":"p1"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "3:28::30","statementOrExpressions":[]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/min_max.go.source b/sonar-go-to-slang/resources/ast/min_max.go.source new file mode 100644 index 00000000..f2e0ea83 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/min_max.go.source @@ -0,0 +1,15 @@ +package ast + +func minMax() { + + var x, y int + m := min(x) // m == x + m := min(x, y) // m is the smaller of x and y + m := max(x, y, 10) // m is the larger of x and y but at least 10 + c := max(1, 2.0, 10) // c == 10.0 (floating-point kind) + f := max(0, float32(x)) // type of f is float32 + var s []string + _ = min(s...) // invalid: slice arguments are not permitted + t := max("", "foo", "bar") // t == "foo" (string kind) + +} \ No newline at end of file diff --git a/sonar-go-to-slang/resources/ast/min_max.json b/sonar-go-to-slang/resources/ast/min_max.json new file mode 100644 index 00000000..e62c9104 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/min_max.json @@ -0,0 +1,226 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// m == x", "contentText":" m == x", "range":"6:32::41", "contentRange": "6:34::41"}, + {"text":"// m is the smaller of x and y", "contentText":" m is the smaller of x and y", "range":"7:32::62", "contentRange": "7:34::62"}, + {"text":"// m is the larger of x and y but at least 10", "contentText":" m is the larger of x and y but at least 10", "range":"8:32::77", "contentRange": "8:34::77"}, + {"text":"// c == 10.0 (floating-point kind)", "contentText":" c == 10.0 (floating-point kind)", "range":"9:32::66", "contentRange": "9:34::66"}, + {"text":"// type of f is float32", "contentText":" type of f is float32", "range":"10:32::55", "contentRange": "10:34::55"}, + {"text":"// invalid: slice arguments are not permitted", "contentText":" invalid: slice arguments are not permitted", "range":"12:32::77", "contentRange": "12:34::77"}, + {"text":"// t == \"foo\" (string kind)", "contentText":" t == \"foo\" (string kind)", "range":"13:32::59", "contentRange": "13:34::59"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"}, + {"text":"func","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"minMax","textRange":"3:5::11"}, + {"text":"(","textRange":"3:11::12"}, + {"text":")","textRange":"3:12::13"}, + {"text":"{","textRange":"3:14::15"}, + {"text":"var","textRange":"5:4::7","type":"KEYWORD"}, + {"text":"x","textRange":"5:8::9"}, + {"text":"y","textRange":"5:11::12"}, + {"text":",","textRange":"5:9::10"}, + {"text":"int","textRange":"5:13::16"}, + {"text":"m","textRange":"6:4::5"}, + {"text":":=","textRange":"6:6::8"}, + {"text":"min","textRange":"6:9::12"}, + {"text":"(","textRange":"6:12::13"}, + {"text":"x","textRange":"6:13::14"}, + {"text":")","textRange":"6:14::15"}, + {"text":"m","textRange":"7:4::5"}, + {"text":":=","textRange":"7:6::8"}, + {"text":"min","textRange":"7:9::12"}, + {"text":"(","textRange":"7:12::13"}, + {"text":"x","textRange":"7:13::14"}, + {"text":"y","textRange":"7:16::17"}, + {"text":",","textRange":"7:14::15"}, + {"text":")","textRange":"7:17::18"}, + {"text":"m","textRange":"8:4::5"}, + {"text":":=","textRange":"8:6::8"}, + {"text":"max","textRange":"8:9::12"}, + {"text":"(","textRange":"8:12::13"}, + {"text":"x","textRange":"8:13::14"}, + {"text":"y","textRange":"8:16::17"}, + {"text":",","textRange":"8:14::15"}, + {"text":"10","textRange":"8:19::21"}, + {"text":",","textRange":"8:17::18"}, + {"text":")","textRange":"8:21::22"}, + {"text":"c","textRange":"9:4::5"}, + {"text":":=","textRange":"9:6::8"}, + {"text":"max","textRange":"9:9::12"}, + {"text":"(","textRange":"9:12::13"}, + {"text":"1","textRange":"9:13::14"}, + {"text":"2.0","textRange":"9:16::19"}, + {"text":",","textRange":"9:14::15"}, + {"text":"10","textRange":"9:21::23"}, + {"text":",","textRange":"9:19::20"}, + {"text":")","textRange":"9:23::24"}, + {"text":"f","textRange":"10:4::5"}, + {"text":":=","textRange":"10:6::8"}, + {"text":"max","textRange":"10:9::12"}, + {"text":"(","textRange":"10:12::13"}, + {"text":"0","textRange":"10:13::14"}, + {"text":"float32","textRange":"10:16::23"}, + {"text":"(","textRange":"10:23::24"}, + {"text":"x","textRange":"10:24::25"}, + {"text":")","textRange":"10:25::26"}, + {"text":",","textRange":"10:14::15"}, + {"text":")","textRange":"10:26::27"}, + {"text":"var","textRange":"11:4::7","type":"KEYWORD"}, + {"text":"s","textRange":"11:8::9"}, + {"text":"[","textRange":"11:10::11"}, + {"text":"string","textRange":"11:12::18"}, + {"text":"]","textRange":"11:11::12"}, + {"text":"_","textRange":"12:4::5","type":"KEYWORD"}, + {"text":"=","textRange":"12:6::7"}, + {"text":"min","textRange":"12:8::11"}, + {"text":"(","textRange":"12:11::12"}, + {"text":"s","textRange":"12:12::13"}, + {"text":"...","textRange":"12:13::16"}, + {"text":")","textRange":"12:16::17"}, + {"text":"t","textRange":"13:4::5"}, + {"text":":=","textRange":"13:6::8"}, + {"text":"max","textRange":"13:9::12"}, + {"text":"(","textRange":"13:12::13"}, + {"text":"\"\"","textRange":"13:13::15","type":"STRING_LITERAL"}, + {"text":"\"foo\"","textRange":"13:17::22","type":"STRING_LITERAL"}, + {"text":",","textRange":"13:15::16"}, + {"text":"\"bar\"","textRange":"13:24::29","type":"STRING_LITERAL"}, + {"text":",","textRange":"13:22::23"}, + {"text":")","textRange":"13:29::30"}, + {"text":"}","textRange":"15:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:15:1","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "3:0:15:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "3:5::11","name":"minMax"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "3:14:15:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "5:4::16","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "Native", "metaData": "5:4::16","nativeKind":"Decl(GenDecl)","children":[ + {"@type": "Native", "metaData": "5:4::7","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "5:8::16","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "5:8::16","nativeKind":"[0](ValueSpec)","children":[ + {"@type": "Native", "metaData": "5:8::12","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "5:8::9","name":"x"}, + {"@type": "Native", "metaData": "5:9::10"}, + {"@type": "Identifier", "metaData": "5:11::12","name":"y"} + ]}, + {"@type": "Identifier", "metaData": "5:13::16","name":"int"} + ]} + ]} + ]} + ]}, + {"@type": "VariableDeclaration", "metaData": "6:4::15","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "6:9::15","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "6:9::12","name":"min"}, + {"@type": "Native", "metaData": "6:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "6:13::14","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "6:13::14","name":"x"} + ]}, + {"@type": "Native", "metaData": "6:14::15","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "6:4::5","name":"m"}}, + {"@type": "VariableDeclaration", "metaData": "7:4::18","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "7:9::18","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "7:9::12","name":"min"}, + {"@type": "Native", "metaData": "7:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "7:13::17","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "7:13::14","name":"x"}, + {"@type": "Native", "metaData": "7:14::15"}, + {"@type": "Identifier", "metaData": "7:16::17","name":"y"} + ]}, + {"@type": "Native", "metaData": "7:17::18","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "7:4::5","name":"m"}}, + {"@type": "VariableDeclaration", "metaData": "8:4::22","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "8:9::22","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "8:9::12","name":"max"}, + {"@type": "Native", "metaData": "8:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "8:13::21","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "8:13::14","name":"x"}, + {"@type": "Native", "metaData": "8:14::15"}, + {"@type": "Identifier", "metaData": "8:16::17","name":"y"}, + {"@type": "Native", "metaData": "8:17::18"}, + {"@type": "IntegerLiteral", "metaData": "8:19::21","value":"10"} + ]}, + {"@type": "Native", "metaData": "8:21::22","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "8:4::5","name":"m"}}, + {"@type": "VariableDeclaration", "metaData": "9:4::24","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "9:9::24","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "9:9::12","name":"max"}, + {"@type": "Native", "metaData": "9:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "9:13::23","nativeKind":"Args([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "9:13::14","value":"1"}, + {"@type": "Native", "metaData": "9:14::15"}, + {"@type": "Native", "metaData": "9:16::19","nativeKind":"[1](BasicLit)"}, + {"@type": "Native", "metaData": "9:19::20"}, + {"@type": "IntegerLiteral", "metaData": "9:21::23","value":"10"} + ]}, + {"@type": "Native", "metaData": "9:23::24","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "9:4::5","name":"c"}}, + {"@type": "VariableDeclaration", "metaData": "10:4::27","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "10:9::27","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "10:9::12","name":"max"}, + {"@type": "Native", "metaData": "10:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "10:13::26","nativeKind":"Args([]Expr)","children":[ + {"@type": "IntegerLiteral", "metaData": "10:13::14","value":"0"}, + {"@type": "Native", "metaData": "10:14::15"}, + {"@type": "Native", "metaData": "10:16::26","nativeKind":"[1](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "10:16::23","name":"float32"}, + {"@type": "Native", "metaData": "10:23::24","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "10:24::25","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "10:24::25","name":"x"} + ]}, + {"@type": "Native", "metaData": "10:25::26","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "10:26::27","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "10:4::5","name":"f"}}, + {"@type": "Native", "metaData": "11:4::18","nativeKind":"[6](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "11:4::18","type": + {"@type": "Native", "metaData": "11:10::18","nativeKind":"Type(ArrayType)","children":[ + {"@type": "Native", "metaData": "11:10::11","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "11:11::12"}, + {"@type": "Identifier", "metaData": "11:12::18","name":"string"} + ]},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "11:8::9","name":"s"}} + ]}, + {"@type": "AssignmentExpression", "metaData": "12:4::17","statementOrExpression": + {"@type": "Native", "metaData": "12:8::17","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "12:8::11","name":"min"}, + {"@type": "Native", "metaData": "12:11::12","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "12:12::13","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "12:12::13","name":"s"} + ]}, + {"@type": "Native", "metaData": "12:13::16","nativeKind":"Ellipsis"}, + {"@type": "Native", "metaData": "12:16::17","nativeKind":"Rparen"} + ]},"operator":"EQUAL","leftHandSide": + {"@type": "PlaceHolder", "metaData": "12:4::5","placeHolderToken":"12:4::5"}}, + {"@type": "VariableDeclaration", "metaData": "13:4::30","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "13:9::30","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "13:9::12","name":"max"}, + {"@type": "Native", "metaData": "13:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "13:13::29","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "13:13::15","value":"\"\"","content":""}, + {"@type": "Native", "metaData": "13:15::16"}, + {"@type": "StringLiteral", "metaData": "13:17::22","value":"\"foo\"","content":"foo"}, + {"@type": "Native", "metaData": "13:22::23"}, + {"@type": "StringLiteral", "metaData": "13:24::29","value":"\"bar\"","content":"bar"} + ]}, + {"@type": "Native", "metaData": "13:29::30","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "13:4::5","name":"t"}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/placeholder.go.source b/sonar-go-to-slang/resources/ast/placeholder.go.source new file mode 100644 index 00000000..04e14c1a --- /dev/null +++ b/sonar-go-to-slang/resources/ast/placeholder.go.source @@ -0,0 +1,23 @@ +package ast + + var _ int = 1 // This is not a VariableDeclaration + + func bar() { + _, s := foo() + i, _ := foo() + // _ := "42" is a compilation error + _, _ = foo() + _ = 22 + } + + func bar2(_, _ int) bool { // These are not two parameters + return false + } + + func foo() (int, string) { + return 42, "42" + } + + func _() { + // Empty + } diff --git a/sonar-go-to-slang/resources/ast/placeholder.json b/sonar-go-to-slang/resources/ast/placeholder.json new file mode 100644 index 00000000..f981b8f2 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/placeholder.json @@ -0,0 +1,186 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// This is not a VariableDeclaration", "contentText":" This is not a VariableDeclaration", "range":"3:15::51", "contentRange": "3:17::51"}, + {"text":"// _ := \"42\" is a compilation error", "contentText":" _ := \"42\" is a compilation error", "range":"8:5::40", "contentRange": "8:7::40"}, + {"text":"// These are not two parameters", "contentText":" These are not two parameters", "range":"13:28::59", "contentRange": "13:30::59"}, + {"text":"// Empty", "contentText":" Empty", "range":"22:4::12", "contentRange": "22:6::12"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"ast","textRange":"1:8::11"}, + {"text":"var","textRange":"3:1::4","type":"KEYWORD"}, + {"text":"_","textRange":"3:5::6","type":"KEYWORD"}, + {"text":"int","textRange":"3:7::10"}, + {"text":"1","textRange":"3:13::14"}, + {"text":"=","textRange":"3:11::12"}, + {"text":"func","textRange":"5:1::5","type":"KEYWORD"}, + {"text":"bar","textRange":"5:6::9"}, + {"text":"(","textRange":"5:9::10"}, + {"text":")","textRange":"5:10::11"}, + {"text":"{","textRange":"5:12::13"}, + {"text":"_","textRange":"6:5::6","type":"KEYWORD"}, + {"text":"s","textRange":"6:8::9"}, + {"text":",","textRange":"6:6::7"}, + {"text":":=","textRange":"6:10::12"}, + {"text":"foo","textRange":"6:13::16"}, + {"text":"(","textRange":"6:16::17"}, + {"text":")","textRange":"6:17::18"}, + {"text":"i","textRange":"7:5::6"}, + {"text":"_","textRange":"7:8::9","type":"KEYWORD"}, + {"text":",","textRange":"7:6::7"}, + {"text":":=","textRange":"7:10::12"}, + {"text":"foo","textRange":"7:13::16"}, + {"text":"(","textRange":"7:16::17"}, + {"text":")","textRange":"7:17::18"}, + {"text":"_","textRange":"9:5::6","type":"KEYWORD"}, + {"text":"_","textRange":"9:8::9","type":"KEYWORD"}, + {"text":",","textRange":"9:6::7"}, + {"text":"=","textRange":"9:10::11"}, + {"text":"foo","textRange":"9:12::15"}, + {"text":"(","textRange":"9:15::16"}, + {"text":")","textRange":"9:16::17"}, + {"text":"_","textRange":"10:5::6","type":"KEYWORD"}, + {"text":"=","textRange":"10:7::8"}, + {"text":"22","textRange":"10:9::11"}, + {"text":"}","textRange":"11:1::2"}, + {"text":"func","textRange":"13:1::5","type":"KEYWORD"}, + {"text":"bar2","textRange":"13:6::10"}, + {"text":"(","textRange":"13:10::11"}, + {"text":"_","textRange":"13:11::12","type":"KEYWORD"}, + {"text":"_","textRange":"13:14::15","type":"KEYWORD"}, + {"text":"int","textRange":"13:16::19"}, + {"text":",","textRange":"13:12::13"}, + {"text":")","textRange":"13:19::20"}, + {"text":"bool","textRange":"13:21::25"}, + {"text":"{","textRange":"13:26::27"}, + {"text":"return","textRange":"14:4::10","type":"KEYWORD"}, + {"text":"false","textRange":"14:11::16"}, + {"text":"}","textRange":"15:1::2"}, + {"text":"func","textRange":"17:1::5","type":"KEYWORD"}, + {"text":"foo","textRange":"17:6::9"}, + {"text":"(","textRange":"17:9::10"}, + {"text":")","textRange":"17:10::11"}, + {"text":"(","textRange":"17:12::13"}, + {"text":"int","textRange":"17:13::16"}, + {"text":"string","textRange":"17:18::24"}, + {"text":",","textRange":"17:16::17"}, + {"text":")","textRange":"17:24::25"}, + {"text":"{","textRange":"17:26::27"}, + {"text":"return","textRange":"18:5::11","type":"KEYWORD"}, + {"text":"42","textRange":"18:12::14"}, + {"text":"\"42\"","textRange":"18:16::20","type":"STRING_LITERAL"}, + {"text":",","textRange":"18:14::15"}, + {"text":"}","textRange":"19:1::2"}, + {"text":"func","textRange":"21:1::5","type":"KEYWORD"}, + {"text":"_","textRange":"21:6::7","type":"KEYWORD"}, + {"text":"(","textRange":"21:7::8"}, + {"text":")","textRange":"21:8::9"}, + {"text":"{","textRange":"21:10::11"}, + {"text":"}","textRange":"23:1::2"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:24:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::11","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::11","name":"ast"} + ]}, + {"@type": "VariableDeclaration", "metaData": "3:1::14","type": + {"@type": "Identifier", "metaData": "3:7::10","name":"int"},"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "3:13::14","value":"1"},"identifier": + {"@type": "PlaceHolder", "metaData": "3:5::6","placeHolderToken":"3:5::6"}}, + {"@type": "FunctionDeclaration", "metaData": "5:1:11:2","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "5:6::9","name":"bar"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "5:12:11:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "6:5::18","nativeKind":"[0](AssignStmt)","children":[ + {"@type": "Native", "metaData": "6:5::9","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "PlaceHolder", "metaData": "6:5::6","placeHolderToken":"6:5::6"}, + {"@type": "Native", "metaData": "6:6::7"}, + {"@type": "Identifier", "metaData": "6:8::9","name":"s"} + ]}, + {"@type": "Native", "metaData": "6:10::12","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "6:13::18","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "Native", "metaData": "6:13::18","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "6:13::16","name":"foo"}, + {"@type": "Native", "metaData": "6:16::17","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "6:17::18","nativeKind":"Rparen"} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "7:5::18","nativeKind":"[1](AssignStmt)","children":[ + {"@type": "Native", "metaData": "7:5::9","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "7:5::6","name":"i"}, + {"@type": "Native", "metaData": "7:6::7"}, + {"@type": "PlaceHolder", "metaData": "7:8::9","placeHolderToken":"7:8::9"} + ]}, + {"@type": "Native", "metaData": "7:10::12","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "7:13::18","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "Native", "metaData": "7:13::18","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "7:13::16","name":"foo"}, + {"@type": "Native", "metaData": "7:16::17","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "7:17::18","nativeKind":"Rparen"} + ]} + ]} + ]}, + {"@type": "AssignmentExpression", "metaData": "9:5::17","statementOrExpression": + {"@type": "Native", "metaData": "9:12::17","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "9:12::15","name":"foo"}, + {"@type": "Native", "metaData": "9:15::16","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "9:16::17","nativeKind":"Rparen"} + ]},"operator":"EQUAL","leftHandSide": + {"@type": "Native", "metaData": "9:5::9","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "PlaceHolder", "metaData": "9:5::6","placeHolderToken":"9:5::6"}, + {"@type": "Native", "metaData": "9:6::7"}, + {"@type": "PlaceHolder", "metaData": "9:8::9","placeHolderToken":"9:8::9"} + ]}}, + {"@type": "AssignmentExpression", "metaData": "10:5::11","statementOrExpression": + {"@type": "IntegerLiteral", "metaData": "10:9::11","value":"22"},"operator":"EQUAL","leftHandSide": + {"@type": "PlaceHolder", "metaData": "10:5::6","placeHolderToken":"10:5::6"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "13:1:15:2","returnType": + {"@type": "Native", "metaData": "13:21::25","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "13:21::25","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "13:21::25","name":"bool"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "13:6::10","name":"bar2"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "13:11::12","type": + null,"modifiers":null,"identifier": + {"@type": "PlaceHolder", "metaData": "13:11::12","placeHolderToken":"13:11::12"},"defaultValue":null}, + {"@type": "Parameter", "metaData": "13:14::19","type": + {"@type": "Identifier", "metaData": "13:16::19","name":"int"},"modifiers":null,"identifier": + {"@type": "PlaceHolder", "metaData": "13:14::15","placeHolderToken":"13:14::15"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "13:26:15:2","statementOrExpressions":[ + {"@type": "Return", "metaData": "14:4::16","keyword":"14:4::10","body": + {"@type": "Literal", "metaData": "14:11::16","value":"false"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "17:1:19:2","returnType": + {"@type": "Native", "metaData": "17:12::25","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "17:12::13","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "17:13::16","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "17:13::16","name":"int"} + ]}, + {"@type": "Native", "metaData": "17:16::17"}, + {"@type": "Native", "metaData": "17:18::24","nativeKind":"[1](Field)","children":[ + {"@type": "Identifier", "metaData": "17:18::24","name":"string"} + ]}, + {"@type": "Native", "metaData": "17:24::25","nativeKind":"Closing"} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "17:6::9","name":"foo"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "17:26:19:2","statementOrExpressions":[ + {"@type": "Return", "metaData": "18:5::20","keyword":"18:5::11","body": + {"@type": "Native", "metaData": "18:12::20","nativeKind":"ReturnExprList","children":[ + {"@type": "IntegerLiteral", "metaData": "18:12::14","value":"42"}, + {"@type": "Native", "metaData": "18:14::15"}, + {"@type": "StringLiteral", "metaData": "18:16::20","value":"\"42\"","content":"42"} + ]}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "21:1:23:2","returnType": + null,"nativeChildren":[],"name": + {"@type": "PlaceHolder", "metaData": "21:6::7","placeHolderToken":"21:6::7"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "21:10:23:2","statementOrExpressions":[]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/return.go.source b/sonar-go-to-slang/resources/ast/return.go.source new file mode 100644 index 00000000..0124d292 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/return.go.source @@ -0,0 +1,22 @@ +package main + +func returnTrue() bool { + return true +} + +func emptyReturn() { + return +} + +func twoReturn() string { + if true { + return "true" + } else { + return "false" + } +} + +func multipleReturnValue() (string, int, string) { + b := "b" + return b, 2, "c" // Expressions are wrapped in a native node, and this node is used as body of the return tree +} diff --git a/sonar-go-to-slang/resources/ast/return.json b/sonar-go-to-slang/resources/ast/return.json new file mode 100644 index 00000000..fa700229 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/return.json @@ -0,0 +1,140 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// Expressions are wrapped in a native node, and this node is used as body of the return tree", "contentText":" Expressions are wrapped in a native node, and this node is used as body of the return tree", "range":"21:18::111", "contentRange": "21:20::111"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"func","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"returnTrue","textRange":"3:5::15"}, + {"text":"(","textRange":"3:15::16"}, + {"text":")","textRange":"3:16::17"}, + {"text":"bool","textRange":"3:18::22"}, + {"text":"{","textRange":"3:23::24"}, + {"text":"return","textRange":"4:1::7","type":"KEYWORD"}, + {"text":"true","textRange":"4:8::12"}, + {"text":"}","textRange":"5:0::1"}, + {"text":"func","textRange":"7:0::4","type":"KEYWORD"}, + {"text":"emptyReturn","textRange":"7:5::16"}, + {"text":"(","textRange":"7:16::17"}, + {"text":")","textRange":"7:17::18"}, + {"text":"{","textRange":"7:19::20"}, + {"text":"return","textRange":"8:1::7","type":"KEYWORD"}, + {"text":"}","textRange":"9:0::1"}, + {"text":"func","textRange":"11:0::4","type":"KEYWORD"}, + {"text":"twoReturn","textRange":"11:5::14"}, + {"text":"(","textRange":"11:14::15"}, + {"text":")","textRange":"11:15::16"}, + {"text":"string","textRange":"11:17::23"}, + {"text":"{","textRange":"11:24::25"}, + {"text":"if","textRange":"12:1::3","type":"KEYWORD"}, + {"text":"true","textRange":"12:4::8"}, + {"text":"{","textRange":"12:9::10"}, + {"text":"return","textRange":"13:2::8","type":"KEYWORD"}, + {"text":"\"true\"","textRange":"13:9::15","type":"STRING_LITERAL"}, + {"text":"}","textRange":"14:1::2"}, + {"text":"{","textRange":"14:8::9"}, + {"text":"return","textRange":"15:2::8","type":"KEYWORD"}, + {"text":"\"false\"","textRange":"15:9::16","type":"STRING_LITERAL"}, + {"text":"}","textRange":"16:1::2"}, + {"text":"else","textRange":"14:3::7","type":"KEYWORD"}, + {"text":"}","textRange":"17:0::1"}, + {"text":"func","textRange":"19:0::4","type":"KEYWORD"}, + {"text":"multipleReturnValue","textRange":"19:5::24"}, + {"text":"(","textRange":"19:24::25"}, + {"text":")","textRange":"19:25::26"}, + {"text":"(","textRange":"19:27::28"}, + {"text":"string","textRange":"19:28::34"}, + {"text":"int","textRange":"19:36::39"}, + {"text":",","textRange":"19:34::35"}, + {"text":"string","textRange":"19:41::47"}, + {"text":",","textRange":"19:39::40"}, + {"text":")","textRange":"19:47::48"}, + {"text":"{","textRange":"19:49::50"}, + {"text":"b","textRange":"20:1::2"}, + {"text":":=","textRange":"20:3::5"}, + {"text":"\"b\"","textRange":"20:6::9","type":"STRING_LITERAL"}, + {"text":"return","textRange":"21:1::7","type":"KEYWORD"}, + {"text":"b","textRange":"21:8::9"}, + {"text":"2","textRange":"21:11::12"}, + {"text":",","textRange":"21:9::10"}, + {"text":"\"c\"","textRange":"21:14::17","type":"STRING_LITERAL"}, + {"text":",","textRange":"21:12::13"}, + {"text":"}","textRange":"22:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:23:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "3:0:5:1","returnType": + {"@type": "Native", "metaData": "3:18::22","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "3:18::22","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "3:18::22","name":"bool"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "3:5::15","name":"returnTrue"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "3:23:5:1","statementOrExpressions":[ + {"@type": "Return", "metaData": "4:1::12","keyword":"4:1::7","body": + {"@type": "Literal", "metaData": "4:8::12","value":"true"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "7:0:9:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "7:5::16","name":"emptyReturn"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "7:19:9:1","statementOrExpressions":[ + {"@type": "Return", "metaData": "8:1::7","keyword":"8:1::7","body":null} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "11:0:17:1","returnType": + {"@type": "Native", "metaData": "11:17::23","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "11:17::23","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "11:17::23","name":"string"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "11:5::14","name":"twoReturn"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "11:24:17:1","statementOrExpressions":[ + {"@type": "If", "metaData": "12:1:16:2","thenBranch": + {"@type": "Block", "metaData": "12:9:14:2","statementOrExpressions":[ + {"@type": "Return", "metaData": "13:2::15","keyword":"13:2::8","body": + {"@type": "StringLiteral", "metaData": "13:9::15","value":"\"true\"","content":"true"}} + ]},"ifKeyword":"12:1::3","elseKeyword":"14:3::7","elseBranch": + {"@type": "Block", "metaData": "14:8:16:2","statementOrExpressions":[ + {"@type": "Return", "metaData": "15:2::16","keyword":"15:2::8","body": + {"@type": "StringLiteral", "metaData": "15:9::16","value":"\"false\"","content":"false"}} + ]},"condition": + {"@type": "Literal", "metaData": "12:4::8","value":"true"}} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "19:0:22:1","returnType": + {"@type": "Native", "metaData": "19:27::48","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "19:27::28","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "19:28::34","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "19:28::34","name":"string"} + ]}, + {"@type": "Native", "metaData": "19:34::35"}, + {"@type": "Native", "metaData": "19:36::39","nativeKind":"[1](Field)","children":[ + {"@type": "Identifier", "metaData": "19:36::39","name":"int"} + ]}, + {"@type": "Native", "metaData": "19:39::40"}, + {"@type": "Native", "metaData": "19:41::47","nativeKind":"[2](Field)","children":[ + {"@type": "Identifier", "metaData": "19:41::47","name":"string"} + ]}, + {"@type": "Native", "metaData": "19:47::48","nativeKind":"Closing"} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "19:5::24","name":"multipleReturnValue"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "19:49:22:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "20:1::9","type":null,"isVal":false,"initializer": + {"@type": "StringLiteral", "metaData": "20:6::9","value":"\"b\"","content":"b"},"identifier": + {"@type": "Identifier", "metaData": "20:1::2","name":"b"}}, + {"@type": "Return", "metaData": "21:1::17","keyword":"21:1::7","body": + {"@type": "Native", "metaData": "21:8::17","nativeKind":"ReturnExprList","children":[ + {"@type": "Identifier", "metaData": "21:8::9","name":"b"}, + {"@type": "Native", "metaData": "21:9::10"}, + {"@type": "IntegerLiteral", "metaData": "21:11::12","value":"2"}, + {"@type": "Native", "metaData": "21:12::13"}, + {"@type": "StringLiteral", "metaData": "21:14::17","value":"\"c\"","content":"c"} + ]}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/select.go.source b/sonar-go-to-slang/resources/ast/select.go.source new file mode 100644 index 00000000..d2b1c038 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/select.go.source @@ -0,0 +1,65 @@ +package main + +//Select are not translated to MatchTree + +import ( + "fmt" + "time" +) + +func server1(ch chan string) { + time.Sleep(6 * time.Second) + ch <- "from server1" +} +func server2(ch chan string) { + time.Sleep(3 * time.Second) + ch <- "from server2" + +} +func main() { + output1 := make(chan string) + output2 := make(chan string) + go server1(output1) + go server2(output2) + select { + case s1 := <-output1: + fmt.Println(s1) + break + case s2 := <-output2: + fmt.Println(s2) + } +} + + +func f2() { + var a []int + var c, c1, c2, c3, c4 chan int + var i1, i2 int + select { + case i1 = <-c1: + print("received ", i1, " from c1\n") + case c2 <- i2: + print("sent ", i2, " to c2\n") + case i3, ok := (<-c3): // same as: i3, ok := <-c3 + if ok { + print("received ", i3, " from c3\n") + } else { + print("c3 is closed\n") + } + case a[f()] = <-c4: + // same as: + // case t := <-c4 + // a[f()] = t + default: + print("no communication\n") + } + + for { // send random sequence of bits to c + select { + case c <- 0: // note: no statement, no fallthrough, no folding of cases + case c <- 1: + } + } + + select {} // block forever +} \ No newline at end of file diff --git a/sonar-go-to-slang/resources/ast/select.json b/sonar-go-to-slang/resources/ast/select.json new file mode 100644 index 00000000..6f24ccaf --- /dev/null +++ b/sonar-go-to-slang/resources/ast/select.json @@ -0,0 +1,677 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"//Select are not translated to MatchTree", "contentText":"Select are not translated to MatchTree", "range":"3:0::40", "contentRange": "3:2::40"}, + {"text":"// same as: i3, ok := \u003c-c3", "contentText":" same as: i3, ok := \u003c-c3", "range":"43:28::54", "contentRange": "43:30::54"}, + {"text":"// same as:", "contentText":" same as:", "range":"50:8::19", "contentRange": "50:10::19"}, + {"text":"// case t := \u003c-c4", "contentText":" case t := \u003c-c4", "range":"51:8::25", "contentRange": "51:10::25"}, + {"text":"//\ta[f()] = t", "contentText":"\ta[f()] = t", "range":"52:8::21", "contentRange": "52:10::21"}, + {"text":"// send random sequence of bits to c", "contentText":" send random sequence of bits to c", "range":"57:11::47", "contentRange": "57:13::47"}, + {"text":"// note: no statement, no fallthrough, no folding of cases", "contentText":" note: no statement, no fallthrough, no folding of cases", "range":"59:22::80", "contentRange": "59:24::80"}, + {"text":"// block forever", "contentText":" block forever", "range":"64:15::31", "contentRange": "64:17::31"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"import","textRange":"5:0::6","type":"KEYWORD"}, + {"text":"(","textRange":"5:7::8"}, + {"text":"\"fmt\"","textRange":"6:1::6","type":"STRING_LITERAL"}, + {"text":"\"time\"","textRange":"7:1::7","type":"STRING_LITERAL"}, + {"text":")","textRange":"8:0::1"}, + {"text":"func","textRange":"10:0::4","type":"KEYWORD"}, + {"text":"server1","textRange":"10:5::12"}, + {"text":"(","textRange":"10:12::13"}, + {"text":"ch","textRange":"10:13::15"}, + {"text":"string","textRange":"10:21::27"}, + {"text":"chan","textRange":"10:16::20","type":"KEYWORD"}, + {"text":")","textRange":"10:27::28"}, + {"text":"{","textRange":"10:29::30"}, + {"text":"time","textRange":"11:1::5"}, + {"text":"Sleep","textRange":"11:6::11"}, + {"text":".","textRange":"11:5::6"}, + {"text":"(","textRange":"11:11::12"}, + {"text":"6","textRange":"11:12::13"}, + {"text":"*","textRange":"11:14::15"}, + {"text":"time","textRange":"11:16::20"}, + {"text":"Second","textRange":"11:21::27"}, + {"text":".","textRange":"11:20::21"}, + {"text":")","textRange":"11:27::28"}, + {"text":"ch","textRange":"12:1::3"}, + {"text":"\u003c-","textRange":"12:4::6"}, + {"text":"\"from server1\"","textRange":"12:7::21","type":"STRING_LITERAL"}, + {"text":"}","textRange":"13:0::1"}, + {"text":"func","textRange":"14:0::4","type":"KEYWORD"}, + {"text":"server2","textRange":"14:5::12"}, + {"text":"(","textRange":"14:12::13"}, + {"text":"ch","textRange":"14:13::15"}, + {"text":"string","textRange":"14:21::27"}, + {"text":"chan","textRange":"14:16::20","type":"KEYWORD"}, + {"text":")","textRange":"14:27::28"}, + {"text":"{","textRange":"14:29::30"}, + {"text":"time","textRange":"15:1::5"}, + {"text":"Sleep","textRange":"15:6::11"}, + {"text":".","textRange":"15:5::6"}, + {"text":"(","textRange":"15:11::12"}, + {"text":"3","textRange":"15:12::13"}, + {"text":"*","textRange":"15:14::15"}, + {"text":"time","textRange":"15:16::20"}, + {"text":"Second","textRange":"15:21::27"}, + {"text":".","textRange":"15:20::21"}, + {"text":")","textRange":"15:27::28"}, + {"text":"ch","textRange":"16:1::3"}, + {"text":"\u003c-","textRange":"16:4::6"}, + {"text":"\"from server2\"","textRange":"16:7::21","type":"STRING_LITERAL"}, + {"text":"}","textRange":"18:0::1"}, + {"text":"func","textRange":"19:0::4","type":"KEYWORD"}, + {"text":"main","textRange":"19:5::9"}, + {"text":"(","textRange":"19:9::10"}, + {"text":")","textRange":"19:10::11"}, + {"text":"{","textRange":"19:12::13"}, + {"text":"output1","textRange":"20:1::8"}, + {"text":":=","textRange":"20:9::11"}, + {"text":"make","textRange":"20:12::16"}, + {"text":"(","textRange":"20:16::17"}, + {"text":"string","textRange":"20:22::28"}, + {"text":"chan","textRange":"20:17::21","type":"KEYWORD"}, + {"text":")","textRange":"20:28::29"}, + {"text":"output2","textRange":"21:1::8"}, + {"text":":=","textRange":"21:9::11"}, + {"text":"make","textRange":"21:12::16"}, + {"text":"(","textRange":"21:16::17"}, + {"text":"string","textRange":"21:22::28"}, + {"text":"chan","textRange":"21:17::21","type":"KEYWORD"}, + {"text":")","textRange":"21:28::29"}, + {"text":"go","textRange":"22:1::3","type":"KEYWORD"}, + {"text":"server1","textRange":"22:4::11"}, + {"text":"(","textRange":"22:11::12"}, + {"text":"output1","textRange":"22:12::19"}, + {"text":")","textRange":"22:19::20"}, + {"text":"go","textRange":"23:1::3","type":"KEYWORD"}, + {"text":"server2","textRange":"23:4::11"}, + {"text":"(","textRange":"23:11::12"}, + {"text":"output2","textRange":"23:12::19"}, + {"text":")","textRange":"23:19::20"}, + {"text":"select","textRange":"24:1::7","type":"KEYWORD"}, + {"text":"{","textRange":"24:8::9"}, + {"text":"case","textRange":"25:1::5","type":"KEYWORD"}, + {"text":"s1","textRange":"25:6::8"}, + {"text":":=","textRange":"25:9::11"}, + {"text":"\u003c-","textRange":"25:12::14"}, + {"text":"output1","textRange":"25:14::21"}, + {"text":":","textRange":"25:21::22"}, + {"text":"fmt","textRange":"26:2::5"}, + {"text":"Println","textRange":"26:6::13"}, + {"text":".","textRange":"26:5::6"}, + {"text":"(","textRange":"26:13::14"}, + {"text":"s1","textRange":"26:14::16"}, + {"text":")","textRange":"26:16::17"}, + {"text":"break","textRange":"27:2::7","type":"KEYWORD"}, + {"text":"case","textRange":"28:1::5","type":"KEYWORD"}, + {"text":"s2","textRange":"28:6::8"}, + {"text":":=","textRange":"28:9::11"}, + {"text":"\u003c-","textRange":"28:12::14"}, + {"text":"output2","textRange":"28:14::21"}, + {"text":":","textRange":"28:21::22"}, + {"text":"fmt","textRange":"29:2::5"}, + {"text":"Println","textRange":"29:6::13"}, + {"text":".","textRange":"29:5::6"}, + {"text":"(","textRange":"29:13::14"}, + {"text":"s2","textRange":"29:14::16"}, + {"text":")","textRange":"29:16::17"}, + {"text":"}","textRange":"30:1::2"}, + {"text":"}","textRange":"31:0::1"}, + {"text":"func","textRange":"34:0::4","type":"KEYWORD"}, + {"text":"f2","textRange":"34:5::7"}, + {"text":"(","textRange":"34:7::8"}, + {"text":")","textRange":"34:8::9"}, + {"text":"{","textRange":"34:10::11"}, + {"text":"var","textRange":"35:4::7","type":"KEYWORD"}, + {"text":"a","textRange":"35:8::9"}, + {"text":"[","textRange":"35:10::11"}, + {"text":"int","textRange":"35:12::15"}, + {"text":"]","textRange":"35:11::12"}, + {"text":"var","textRange":"36:4::7","type":"KEYWORD"}, + {"text":"c","textRange":"36:8::9"}, + {"text":"c1","textRange":"36:11::13"}, + {"text":",","textRange":"36:9::10"}, + {"text":"c2","textRange":"36:15::17"}, + {"text":",","textRange":"36:13::14"}, + {"text":"c3","textRange":"36:19::21"}, + {"text":",","textRange":"36:17::18"}, + {"text":"c4","textRange":"36:23::25"}, + {"text":",","textRange":"36:21::22"}, + {"text":"int","textRange":"36:31::34"}, + {"text":"chan","textRange":"36:26::30","type":"KEYWORD"}, + {"text":"var","textRange":"37:4::7","type":"KEYWORD"}, + {"text":"i1","textRange":"37:8::10"}, + {"text":"i2","textRange":"37:12::14"}, + {"text":",","textRange":"37:10::11"}, + {"text":"int","textRange":"37:15::18"}, + {"text":"select","textRange":"38:4::10","type":"KEYWORD"}, + {"text":"{","textRange":"38:11::12"}, + {"text":"case","textRange":"39:4::8","type":"KEYWORD"}, + {"text":"i1","textRange":"39:9::11"}, + {"text":"=","textRange":"39:12::13"}, + {"text":"\u003c-","textRange":"39:14::16"}, + {"text":"c1","textRange":"39:16::18"}, + {"text":":","textRange":"39:18::19"}, + {"text":"print","textRange":"40:8::13"}, + {"text":"(","textRange":"40:13::14"}, + {"text":"\"received \"","textRange":"40:14::25","type":"STRING_LITERAL"}, + {"text":"i1","textRange":"40:27::29"}, + {"text":",","textRange":"40:25::26"}, + {"text":"\" from c1\\n\"","textRange":"40:31::43","type":"STRING_LITERAL"}, + {"text":",","textRange":"40:29::30"}, + {"text":")","textRange":"40:43::44"}, + {"text":"case","textRange":"41:4::8","type":"KEYWORD"}, + {"text":"c2","textRange":"41:9::11"}, + {"text":"\u003c-","textRange":"41:12::14"}, + {"text":"i2","textRange":"41:15::17"}, + {"text":":","textRange":"41:17::18"}, + {"text":"print","textRange":"42:8::13"}, + {"text":"(","textRange":"42:13::14"}, + {"text":"\"sent \"","textRange":"42:14::21","type":"STRING_LITERAL"}, + {"text":"i2","textRange":"42:23::25"}, + {"text":",","textRange":"42:21::22"}, + {"text":"\" to c2\\n\"","textRange":"42:27::37","type":"STRING_LITERAL"}, + {"text":",","textRange":"42:25::26"}, + {"text":")","textRange":"42:37::38"}, + {"text":"case","textRange":"43:4::8","type":"KEYWORD"}, + {"text":"i3","textRange":"43:9::11"}, + {"text":"ok","textRange":"43:13::15"}, + {"text":",","textRange":"43:11::12"}, + {"text":":=","textRange":"43:16::18"}, + {"text":"(","textRange":"43:19::20"}, + {"text":"\u003c-","textRange":"43:20::22"}, + {"text":"c3","textRange":"43:22::24"}, + {"text":")","textRange":"43:24::25"}, + {"text":":","textRange":"43:25::26"}, + {"text":"if","textRange":"44:8::10","type":"KEYWORD"}, + {"text":"ok","textRange":"44:11::13"}, + {"text":"{","textRange":"44:14::15"}, + {"text":"print","textRange":"45:12::17"}, + {"text":"(","textRange":"45:17::18"}, + {"text":"\"received \"","textRange":"45:18::29","type":"STRING_LITERAL"}, + {"text":"i3","textRange":"45:31::33"}, + {"text":",","textRange":"45:29::30"}, + {"text":"\" from c3\\n\"","textRange":"45:35::47","type":"STRING_LITERAL"}, + {"text":",","textRange":"45:33::34"}, + {"text":")","textRange":"45:47::48"}, + {"text":"}","textRange":"46:8::9"}, + {"text":"{","textRange":"46:15::16"}, + {"text":"print","textRange":"47:12::17"}, + {"text":"(","textRange":"47:17::18"}, + {"text":"\"c3 is closed\\n\"","textRange":"47:18::34","type":"STRING_LITERAL"}, + {"text":")","textRange":"47:34::35"}, + {"text":"}","textRange":"48:8::9"}, + {"text":"else","textRange":"46:10::14","type":"KEYWORD"}, + {"text":"case","textRange":"49:4::8","type":"KEYWORD"}, + {"text":"a","textRange":"49:9::10"}, + {"text":"[","textRange":"49:10::11"}, + {"text":"f","textRange":"49:11::12"}, + {"text":"(","textRange":"49:12::13"}, + {"text":")","textRange":"49:13::14"}, + {"text":"]","textRange":"49:14::15"}, + {"text":"=","textRange":"49:16::17"}, + {"text":"\u003c-","textRange":"49:18::20"}, + {"text":"c4","textRange":"49:20::22"}, + {"text":":","textRange":"49:22::23"}, + {"text":"default","textRange":"53:4::11","type":"KEYWORD"}, + {"text":":","textRange":"53:11::12"}, + {"text":"print","textRange":"54:8::13"}, + {"text":"(","textRange":"54:13::14"}, + {"text":"\"no communication\\n\"","textRange":"54:14::34","type":"STRING_LITERAL"}, + {"text":")","textRange":"54:34::35"}, + {"text":"}","textRange":"55:4::5"}, + {"text":"for","textRange":"57:4::7","type":"KEYWORD"}, + {"text":"{","textRange":"57:8::9"}, + {"text":"select","textRange":"58:8::14","type":"KEYWORD"}, + {"text":"{","textRange":"58:15::16"}, + {"text":"case","textRange":"59:8::12","type":"KEYWORD"}, + {"text":"c","textRange":"59:13::14"}, + {"text":"\u003c-","textRange":"59:15::17"}, + {"text":"0","textRange":"59:18::19"}, + {"text":":","textRange":"59:19::20"}, + {"text":"case","textRange":"60:8::12","type":"KEYWORD"}, + {"text":"c","textRange":"60:13::14"}, + {"text":"\u003c-","textRange":"60:15::17"}, + {"text":"1","textRange":"60:18::19"}, + {"text":":","textRange":"60:19::20"}, + {"text":"}","textRange":"61:8::9"}, + {"text":"}","textRange":"62:4::5"}, + {"text":"select","textRange":"64:4::10","type":"KEYWORD"}, + {"text":"{","textRange":"64:11::12"}, + {"text":"}","textRange":"64:12::13"}, + {"text":"}","textRange":"65:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:65:1","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "ImportDeclaration", "metaData": "5:0:8:1","children":[ + {"@type": "Native", "metaData": "5:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "5:7::8","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "6:1::6","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "6:1::6","value":"\"fmt\"","content":"fmt"} + ]}, + {"@type": "Native", "metaData": "7:1::7","nativeKind":"[1](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "7:1::7","value":"\"time\"","content":"time"} + ]}, + {"@type": "Native", "metaData": "8:0::1","nativeKind":"Rparen"} + ]}, + {"@type": "FunctionDeclaration", "metaData": "10:0:13:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "10:5::12","name":"server1"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "10:13::27","type": + {"@type": "Native", "metaData": "10:21::27","nativeKind":"Type(ChanType)","children":[ + {"@type": "Identifier", "metaData": "10:21::27","name":"string"} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "10:13::15","name":"ch"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "10:29:13:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "11:1::28","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "11:1::28","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "11:1::11","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "11:1::5","name":"time"}, + {"@type": "Native", "metaData": "11:5::6"}, + {"@type": "Identifier", "metaData": "11:6::11","name":"Sleep"} + ]}, + {"@type": "Native", "metaData": "11:11::12","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "11:12::27","nativeKind":"Args([]Expr)","children":[ + {"@type": "BinaryExpression", "metaData": "11:12::27","rightOperand": + {"@type": "Native", "metaData": "11:16::27","nativeKind":"operand(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "11:16::20","name":"time"}, + {"@type": "Native", "metaData": "11:20::21"}, + {"@type": "Identifier", "metaData": "11:21::27","name":"Second"} + ]},"operatorToken":"11:14::15","operator":"TIMES","leftOperand": + {"@type": "IntegerLiteral", "metaData": "11:12::13","value":"6"}} + ]}, + {"@type": "Native", "metaData": "11:27::28","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "12:1::21","nativeKind":"[1](SendStmt)","children":[ + {"@type": "Identifier", "metaData": "12:1::3","name":"ch"}, + {"@type": "Native", "metaData": "12:4::6","nativeKind":"Arrow"}, + {"@type": "StringLiteral", "metaData": "12:7::21","value":"\"from server1\"","content":"from server1"} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "14:0:18:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "14:5::12","name":"server2"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "14:13::27","type": + {"@type": "Native", "metaData": "14:21::27","nativeKind":"Type(ChanType)","children":[ + {"@type": "Identifier", "metaData": "14:21::27","name":"string"} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "14:13::15","name":"ch"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "14:29:18:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "15:1::28","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "15:1::28","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "15:1::11","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "15:1::5","name":"time"}, + {"@type": "Native", "metaData": "15:5::6"}, + {"@type": "Identifier", "metaData": "15:6::11","name":"Sleep"} + ]}, + {"@type": "Native", "metaData": "15:11::12","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "15:12::27","nativeKind":"Args([]Expr)","children":[ + {"@type": "BinaryExpression", "metaData": "15:12::27","rightOperand": + {"@type": "Native", "metaData": "15:16::27","nativeKind":"operand(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "15:16::20","name":"time"}, + {"@type": "Native", "metaData": "15:20::21"}, + {"@type": "Identifier", "metaData": "15:21::27","name":"Second"} + ]},"operatorToken":"15:14::15","operator":"TIMES","leftOperand": + {"@type": "IntegerLiteral", "metaData": "15:12::13","value":"3"}} + ]}, + {"@type": "Native", "metaData": "15:27::28","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "16:1::21","nativeKind":"[1](SendStmt)","children":[ + {"@type": "Identifier", "metaData": "16:1::3","name":"ch"}, + {"@type": "Native", "metaData": "16:4::6","nativeKind":"Arrow"}, + {"@type": "StringLiteral", "metaData": "16:7::21","value":"\"from server2\"","content":"from server2"} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "19:0:31:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "19:5::9","name":"main"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "19:12:31:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "20:1::29","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "20:12::29","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "20:12::16","name":"make"}, + {"@type": "Native", "metaData": "20:16::17","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "20:17::21"}, + {"@type": "Native", "metaData": "20:22::28","nativeKind":"Args([]Expr)","children":[ + {"@type": "Native", "metaData": "20:22::28","nativeKind":"[0](ChanType)","children":[ + {"@type": "Identifier", "metaData": "20:22::28","name":"string"} + ]} + ]}, + {"@type": "Native", "metaData": "20:28::29","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "20:1::8","name":"output1"}}, + {"@type": "VariableDeclaration", "metaData": "21:1::29","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "21:12::29","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "21:12::16","name":"make"}, + {"@type": "Native", "metaData": "21:16::17","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "21:17::21"}, + {"@type": "Native", "metaData": "21:22::28","nativeKind":"Args([]Expr)","children":[ + {"@type": "Native", "metaData": "21:22::28","nativeKind":"[0](ChanType)","children":[ + {"@type": "Identifier", "metaData": "21:22::28","name":"string"} + ]} + ]}, + {"@type": "Native", "metaData": "21:28::29","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "21:1::8","name":"output2"}}, + {"@type": "Native", "metaData": "22:1::20","nativeKind":"[2](GoStmt)","children":[ + {"@type": "Native", "metaData": "22:1::3","nativeKind":"Go"}, + {"@type": "Native", "metaData": "22:4::20","nativeKind":"Call(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "22:4::11","name":"server1"}, + {"@type": "Native", "metaData": "22:11::12","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "22:12::19","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "22:12::19","name":"output1"} + ]}, + {"@type": "Native", "metaData": "22:19::20","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "23:1::20","nativeKind":"[3](GoStmt)","children":[ + {"@type": "Native", "metaData": "23:1::3","nativeKind":"Go"}, + {"@type": "Native", "metaData": "23:4::20","nativeKind":"Call(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "23:4::11","name":"server2"}, + {"@type": "Native", "metaData": "23:11::12","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "23:12::19","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "23:12::19","name":"output2"} + ]}, + {"@type": "Native", "metaData": "23:19::20","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "24:1:30:2","nativeKind":"[4](SelectStmt)","children":[ + {"@type": "Native", "metaData": "24:1::7","nativeKind":"Select"}, + {"@type": "Block", "metaData": "24:8:30:2","statementOrExpressions":[ + {"@type": "Native", "metaData": "25:1:27:7","nativeKind":"[0](CommClause)","children":[ + {"@type": "Native", "metaData": "25:1::5","nativeKind":"Case"}, + {"@type": "VariableDeclaration", "metaData": "25:6::21","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "25:12::21","nativeKind":"[0](UnaryExpr)","children":[ + {"@type": "Native", "metaData": "25:12::14","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "25:14::21","name":"output1"} + ]},"identifier": + {"@type": "Identifier", "metaData": "25:6::8","name":"s1"}}, + {"@type": "Native", "metaData": "25:21::22","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "26:2:27:7","nativeKind":"Body([]Stmt)","children":[ + {"@type": "Native", "metaData": "26:2::17","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "26:2::17","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "26:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "26:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "26:5::6"}, + {"@type": "Identifier", "metaData": "26:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "26:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "26:14::16","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "26:14::16","name":"s1"} + ]}, + {"@type": "Native", "metaData": "26:16::17","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Jump", "metaData": "27:2::7","label": + null,"kind":"BREAK","keyword":"27:2::7"} + ]} + ]}, + {"@type": "Native", "metaData": "28:1:29:17","nativeKind":"[1](CommClause)","children":[ + {"@type": "Native", "metaData": "28:1::5","nativeKind":"Case"}, + {"@type": "VariableDeclaration", "metaData": "28:6::21","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "28:12::21","nativeKind":"[0](UnaryExpr)","children":[ + {"@type": "Native", "metaData": "28:12::14","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "28:14::21","name":"output2"} + ]},"identifier": + {"@type": "Identifier", "metaData": "28:6::8","name":"s2"}}, + {"@type": "Native", "metaData": "28:21::22","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "29:2::17","nativeKind":"Body([]Stmt)","children":[ + {"@type": "Native", "metaData": "29:2::17","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "29:2::17","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "29:2::13","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "29:2::5","name":"fmt"}, + {"@type": "Native", "metaData": "29:5::6"}, + {"@type": "Identifier", "metaData": "29:6::13","name":"Println"} + ]}, + {"@type": "Native", "metaData": "29:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "29:14::16","nativeKind":"Args([]Expr)","children":[ + {"@type": "Identifier", "metaData": "29:14::16","name":"s2"} + ]}, + {"@type": "Native", "metaData": "29:16::17","nativeKind":"Rparen"} + ]} + ]} + ]} + ]} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "34:0:65:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "34:5::7","name":"f2"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "34:10:65:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "35:4::15","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "35:4::15","type": + {"@type": "Native", "metaData": "35:10::15","nativeKind":"Type(ArrayType)","children":[ + {"@type": "Native", "metaData": "35:10::11","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "35:11::12"}, + {"@type": "Identifier", "metaData": "35:12::15","name":"int"} + ]},"isVal":false,"initializer": + null,"identifier": + {"@type": "Identifier", "metaData": "35:8::9","name":"a"}} + ]}, + {"@type": "Native", "metaData": "36:4::34","nativeKind":"[1](DeclStmt)","children":[ + {"@type": "Native", "metaData": "36:4::34","nativeKind":"Decl(GenDecl)","children":[ + {"@type": "Native", "metaData": "36:4::7","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "36:8::34","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "36:8::34","nativeKind":"[0](ValueSpec)","children":[ + {"@type": "Native", "metaData": "36:8::25","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "36:8::9","name":"c"}, + {"@type": "Native", "metaData": "36:9::10"}, + {"@type": "Identifier", "metaData": "36:11::13","name":"c1"}, + {"@type": "Native", "metaData": "36:13::14"}, + {"@type": "Identifier", "metaData": "36:15::17","name":"c2"}, + {"@type": "Native", "metaData": "36:17::18"}, + {"@type": "Identifier", "metaData": "36:19::21","name":"c3"}, + {"@type": "Native", "metaData": "36:21::22"}, + {"@type": "Identifier", "metaData": "36:23::25","name":"c4"} + ]}, + {"@type": "Native", "metaData": "36:26::30"}, + {"@type": "Native", "metaData": "36:31::34","nativeKind":"Type(ChanType)","children":[ + {"@type": "Identifier", "metaData": "36:31::34","name":"int"} + ]} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "37:4::18","nativeKind":"[2](DeclStmt)","children":[ + {"@type": "Native", "metaData": "37:4::18","nativeKind":"Decl(GenDecl)","children":[ + {"@type": "Native", "metaData": "37:4::7","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "37:8::18","nativeKind":"Specs([]Spec)","children":[ + {"@type": "Native", "metaData": "37:8::18","nativeKind":"[0](ValueSpec)","children":[ + {"@type": "Native", "metaData": "37:8::14","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "37:8::10","name":"i1"}, + {"@type": "Native", "metaData": "37:10::11"}, + {"@type": "Identifier", "metaData": "37:12::14","name":"i2"} + ]}, + {"@type": "Identifier", "metaData": "37:15::18","name":"int"} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "38:4:55:5","nativeKind":"[3](SelectStmt)","children":[ + {"@type": "Native", "metaData": "38:4::10","nativeKind":"Select"}, + {"@type": "Block", "metaData": "38:11:55:5","statementOrExpressions":[ + {"@type": "Native", "metaData": "39:4:40:44","nativeKind":"[0](CommClause)","children":[ + {"@type": "Native", "metaData": "39:4::8","nativeKind":"Case"}, + {"@type": "AssignmentExpression", "metaData": "39:9::18","statementOrExpression": + {"@type": "Native", "metaData": "39:14::18","nativeKind":"[0](UnaryExpr)","children":[ + {"@type": "Native", "metaData": "39:14::16","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "39:16::18","name":"c1"} + ]},"operator":"EQUAL","leftHandSide": + {"@type": "Identifier", "metaData": "39:9::11","name":"i1"}}, + {"@type": "Native", "metaData": "39:18::19","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "40:8::44","nativeKind":"Body([]Stmt)","children":[ + {"@type": "Native", "metaData": "40:8::44","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "40:8::44","nativeKind":"X(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "40:8::13","name":"print"}, + {"@type": "Native", "metaData": "40:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "40:14::43","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "40:14::25","value":"\"received \"","content":"received "}, + {"@type": "Native", "metaData": "40:25::26"}, + {"@type": "Identifier", "metaData": "40:27::29","name":"i1"}, + {"@type": "Native", "metaData": "40:29::30"}, + {"@type": "StringLiteral", "metaData": "40:31::43","value":"\" from c1\\n\"","content":" from c1\\n"} + ]}, + {"@type": "Native", "metaData": "40:43::44","nativeKind":"Rparen"} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "41:4:42:38","nativeKind":"[1](CommClause)","children":[ + {"@type": "Native", "metaData": "41:4::8","nativeKind":"Case"}, + {"@type": "Native", "metaData": "41:9::17","nativeKind":"Comm(SendStmt)","children":[ + {"@type": "Identifier", "metaData": "41:9::11","name":"c2"}, + {"@type": "Native", "metaData": "41:12::14","nativeKind":"Arrow"}, + {"@type": "Identifier", "metaData": "41:15::17","name":"i2"} + ]}, + {"@type": "Native", "metaData": "41:17::18","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "42:8::38","nativeKind":"Body([]Stmt)","children":[ + {"@type": "Native", "metaData": "42:8::38","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "42:8::38","nativeKind":"X(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "42:8::13","name":"print"}, + {"@type": "Native", "metaData": "42:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "42:14::37","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "42:14::21","value":"\"sent \"","content":"sent "}, + {"@type": "Native", "metaData": "42:21::22"}, + {"@type": "Identifier", "metaData": "42:23::25","name":"i2"}, + {"@type": "Native", "metaData": "42:25::26"}, + {"@type": "StringLiteral", "metaData": "42:27::37","value":"\" to c2\\n\"","content":" to c2\\n"} + ]}, + {"@type": "Native", "metaData": "42:37::38","nativeKind":"Rparen"} + ]} + ]} + ]} + ]}, + {"@type": "Native", "metaData": "43:4:48:9","nativeKind":"[2](CommClause)","children":[ + {"@type": "Native", "metaData": "43:4::8","nativeKind":"Case"}, + {"@type": "Native", "metaData": "43:9::25","nativeKind":"Comm(AssignStmt)","children":[ + {"@type": "Native", "metaData": "43:9::15","nativeKind":"Lhs([]Expr)","children":[ + {"@type": "Identifier", "metaData": "43:9::11","name":"i3"}, + {"@type": "Native", "metaData": "43:11::12"}, + {"@type": "Identifier", "metaData": "43:13::15","name":"ok"} + ]}, + {"@type": "Native", "metaData": "43:16::18","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "43:19::25","nativeKind":"Rhs([]Expr)","children":[ + {"@type": "ParenthesizedExpression", "metaData": "43:19::25","rightParenthesis":"43:24::25","leftParenthesis":"43:19::20","expression": + {"@type": "Native", "metaData": "43:20::24","nativeKind":"X(UnaryExpr)","children":[ + {"@type": "Native", "metaData": "43:20::22","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "43:22::24","name":"c3"} + ]}} + ]} + ]}, + {"@type": "Native", "metaData": "43:25::26","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "44:8:48:9","nativeKind":"Body([]Stmt)","children":[ + {"@type": "If", "metaData": "44:8:48:9","thenBranch": + {"@type": "Block", "metaData": "44:14:46:9","statementOrExpressions":[ + {"@type": "Native", "metaData": "45:12::48","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "45:12::48","nativeKind":"X(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "45:12::17","name":"print"}, + {"@type": "Native", "metaData": "45:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "45:18::47","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "45:18::29","value":"\"received \"","content":"received "}, + {"@type": "Native", "metaData": "45:29::30"}, + {"@type": "Identifier", "metaData": "45:31::33","name":"i3"}, + {"@type": "Native", "metaData": "45:33::34"}, + {"@type": "StringLiteral", "metaData": "45:35::47","value":"\" from c3\\n\"","content":" from c3\\n"} + ]}, + {"@type": "Native", "metaData": "45:47::48","nativeKind":"Rparen"} + ]} + ]} + ]},"ifKeyword":"44:8::10","elseKeyword":"46:10::14","elseBranch": + {"@type": "Block", "metaData": "46:15:48:9","statementOrExpressions":[ + {"@type": "Native", "metaData": "47:12::35","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "47:12::35","nativeKind":"X(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "47:12::17","name":"print"}, + {"@type": "Native", "metaData": "47:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "47:18::34","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "47:18::34","value":"\"c3 is closed\\n\"","content":"c3 is closed\\n"} + ]}, + {"@type": "Native", "metaData": "47:34::35","nativeKind":"Rparen"} + ]} + ]} + ]},"condition": + {"@type": "Identifier", "metaData": "44:11::13","name":"ok"}} + ]} + ]}, + {"@type": "Native", "metaData": "49:4::23","nativeKind":"[3](CommClause)","children":[ + {"@type": "Native", "metaData": "49:4::8","nativeKind":"Case"}, + {"@type": "AssignmentExpression", "metaData": "49:9::22","statementOrExpression": + {"@type": "Native", "metaData": "49:18::22","nativeKind":"[0](UnaryExpr)","children":[ + {"@type": "Native", "metaData": "49:18::20","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "49:20::22","name":"c4"} + ]},"operator":"EQUAL","leftHandSide": + {"@type": "Native", "metaData": "49:9::15","nativeKind":"[0](IndexExpr)","children":[ + {"@type": "Identifier", "metaData": "49:9::10","name":"a"}, + {"@type": "Native", "metaData": "49:10::11","nativeKind":"Lbrack"}, + {"@type": "Native", "metaData": "49:11::14","nativeKind":"Index(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "49:11::12","name":"f"}, + {"@type": "Native", "metaData": "49:12::13","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "49:13::14","nativeKind":"Rparen"} + ]}, + {"@type": "Native", "metaData": "49:14::15","nativeKind":"Rbrack"} + ]}}, + {"@type": "Native", "metaData": "49:22::23","nativeKind":"Colon"} + ]}, + {"@type": "Native", "metaData": "53:4:54:35","nativeKind":"[4](CommClause)","children":[ + {"@type": "Native", "metaData": "53:4::11","nativeKind":"Case"}, + {"@type": "Native", "metaData": "53:11::12","nativeKind":"Colon"}, + {"@type": "Native", "metaData": "54:8::35","nativeKind":"Body([]Stmt)","children":[ + {"@type": "Native", "metaData": "54:8::35","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "54:8::35","nativeKind":"X(CallExpr)","children":[ + {"@type": "Identifier", "metaData": "54:8::13","name":"print"}, + {"@type": "Native", "metaData": "54:13::14","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "54:14::34","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "54:14::34","value":"\"no communication\\n\"","content":"no communication\\n"} + ]}, + {"@type": "Native", "metaData": "54:34::35","nativeKind":"Rparen"} + ]} + ]} + ]} + ]} + ]} + ]}, + {"@type": "Loop", "metaData": "57:4:62:5","kind":"WHILE","keyword":"57:4::7","condition": + null,"body": + {"@type": "Block", "metaData": "57:8:62:5","statementOrExpressions":[ + {"@type": "Native", "metaData": "58:8:61:9","nativeKind":"[0](SelectStmt)","children":[ + {"@type": "Native", "metaData": "58:8::14","nativeKind":"Select"}, + {"@type": "Block", "metaData": "58:15:61:9","statementOrExpressions":[ + {"@type": "Native", "metaData": "59:8::20","nativeKind":"[0](CommClause)","children":[ + {"@type": "Native", "metaData": "59:8::12","nativeKind":"Case"}, + {"@type": "Native", "metaData": "59:13::19","nativeKind":"Comm(SendStmt)","children":[ + {"@type": "Identifier", "metaData": "59:13::14","name":"c"}, + {"@type": "Native", "metaData": "59:15::17","nativeKind":"Arrow"}, + {"@type": "IntegerLiteral", "metaData": "59:18::19","value":"0"} + ]}, + {"@type": "Native", "metaData": "59:19::20","nativeKind":"Colon"} + ]}, + {"@type": "Native", "metaData": "60:8::20","nativeKind":"[1](CommClause)","children":[ + {"@type": "Native", "metaData": "60:8::12","nativeKind":"Case"}, + {"@type": "Native", "metaData": "60:13::19","nativeKind":"Comm(SendStmt)","children":[ + {"@type": "Identifier", "metaData": "60:13::14","name":"c"}, + {"@type": "Native", "metaData": "60:15::17","nativeKind":"Arrow"}, + {"@type": "IntegerLiteral", "metaData": "60:18::19","value":"1"} + ]}, + {"@type": "Native", "metaData": "60:19::20","nativeKind":"Colon"} + ]} + ]} + ]} + ]}}, + {"@type": "Native", "metaData": "64:4::13","nativeKind":"[5](SelectStmt)","children":[ + {"@type": "Native", "metaData": "64:4::10","nativeKind":"Select"}, + {"@type": "Block", "metaData": "64:11::13","statementOrExpressions":[]} + ]} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/struct.go.source b/sonar-go-to-slang/resources/ast/struct.go.source new file mode 100644 index 00000000..6558705f --- /dev/null +++ b/sonar-go-to-slang/resources/ast/struct.go.source @@ -0,0 +1,7 @@ +package main + +type AccessRequest struct { + Id string "id" + Name string "name" + Status string "status" +} \ No newline at end of file diff --git a/sonar-go-to-slang/resources/ast/struct.json b/sonar-go-to-slang/resources/ast/struct.json new file mode 100644 index 00000000..05238c5e --- /dev/null +++ b/sonar-go-to-slang/resources/ast/struct.json @@ -0,0 +1,64 @@ +{ + "treeMetaData": { + "comments": [ + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"type","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"AccessRequest","textRange":"3:5::18"}, + {"text":"struct","textRange":"3:19::25","type":"KEYWORD"}, + {"text":"{","textRange":"3:26::27"}, + {"text":"Id","textRange":"4:1::3"}, + {"text":"string","textRange":"4:4::10"}, + {"text":"\"id\"","textRange":"4:11::15"}, + {"text":"Name","textRange":"5:1::5"}, + {"text":"string","textRange":"5:6::12"}, + {"text":"\"name\"","textRange":"5:13::19"}, + {"text":"Status","textRange":"6:1::7"}, + {"text":"string","textRange":"6:8::14"}, + {"text":"\"status\"","textRange":"6:15::23"}, + {"text":"}","textRange":"7:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:7:1","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "ClassDeclaration", "metaData": "3:0:7:1","identifier":"3:5::18","classTree": + {"@type": "Native", "metaData": "3:0:7:1","nativeKind":"[0](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "3:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "3:5::18","name":"AccessRequest"}, + {"@type": "Native", "metaData": "3:19:7:1","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "3:19::25","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "3:26:7:1","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "3:26::27","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "4:1::15","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "4:1::3","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "4:1::3","name":"Id"} + ]}, + {"@type": "Identifier", "metaData": "4:4::10","name":"string"}, + {"@type": "Native", "metaData": "4:11::15","nativeKind":"Tag(BasicLit)"} + ]}, + {"@type": "Native", "metaData": "5:1::19","nativeKind":"[1](Field)","children":[ + {"@type": "Native", "metaData": "5:1::5","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "5:1::5","name":"Name"} + ]}, + {"@type": "Identifier", "metaData": "5:6::12","name":"string"}, + {"@type": "Native", "metaData": "5:13::19","nativeKind":"Tag(BasicLit)"} + ]}, + {"@type": "Native", "metaData": "6:1::23","nativeKind":"[2](Field)","children":[ + {"@type": "Native", "metaData": "6:1::7","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "6:1::7","name":"Status"} + ]}, + {"@type": "Identifier", "metaData": "6:8::14","name":"string"}, + {"@type": "Native", "metaData": "6:15::23","nativeKind":"Tag(BasicLit)"} + ]}, + {"@type": "Native", "metaData": "7:0::1","nativeKind":"Closing"} + ]} + ]} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/switch.go.source b/sonar-go-to-slang/resources/ast/switch.go.source new file mode 100644 index 00000000..41b38ffc --- /dev/null +++ b/sonar-go-to-slang/resources/ast/switch.go.source @@ -0,0 +1,54 @@ +package main + +import "fmt" + +func main() { + tag := 1 + switch tag { + default: fmt.Println("default") + case 0, 1, 2: fmt.Println("123") + case 4, 5, 6, 7: fmt.Println("4567") + } + + x := 4 + y := 2 + z := 3 + + switch { // missing switch expression means "true" + case x < y: fmt.Println("xy") + case x < z: fmt.Println("xz") + case x == 4: fmt.Println("x") + } + + switch x := f(); { + case x < 0: fmt.Println("x<0") + default: fmt.Println("default") + } + + var intType interface{} = "abc" + + switch i := intType.(type) { // This is a TypeSwitchStmt (others are SwitchStmt in this file) + case nil: + fmt.Println("type of i is type of x (interface{})") + break + case int, bool: + fmt.Println("type of i is int or bool") // type of i is type of x (interface{}) + case func(int) float64: + fmt.Println("type of i is func(int)") + fallthrough + case string: { + fmt.Println("type is string and the value is " + i) + } + default: + fmt.Println("don't know the type") + } + + switch { + case x < y: + fmt.Println("xy"); // in Go AST, ";" is in the children of the switch body (Block), and of the Case body + } +} + +func f() int { + return -42 +} \ No newline at end of file diff --git a/sonar-go-to-slang/resources/ast/switch.json b/sonar-go-to-slang/resources/ast/switch.json new file mode 100644 index 00000000..806bc369 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/switch.json @@ -0,0 +1,642 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"// missing switch expression means \"true\"", "contentText":" missing switch expression means \"true\"", "range":"17:13::54", "contentRange": "17:15::54"}, + {"text":"// This is a TypeSwitchStmt (others are SwitchStmt in this file)", "contentText":" This is a TypeSwitchStmt (others are SwitchStmt in this file)", "range":"30:33::97", "contentRange": "30:35::97"}, + {"text":"// type of i is type of x (interface{})", "contentText":" type of i is type of x (interface{})", "range":"35:52::91", "contentRange": "35:54::91"}, + {"text":"// in Go AST, \";\" is in the children of the switch body (Block), and of the Case body", "contentText":" in Go AST, \";\" is in the children of the switch body (Block), and of the Case body", "range":"48:35::120", "contentRange": "48:37::120"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"import","textRange":"3:0::6","type":"KEYWORD"}, + {"text":"\"fmt\"","textRange":"3:7::12","type":"STRING_LITERAL"}, + {"text":"func","textRange":"5:0::4","type":"KEYWORD"}, + {"text":"main","textRange":"5:5::9"}, + {"text":"(","textRange":"5:9::10"}, + {"text":")","textRange":"5:10::11"}, + {"text":"{","textRange":"5:12::13"}, + {"text":"tag","textRange":"6:4::7"}, + {"text":":=","textRange":"6:8::10"}, + {"text":"1","textRange":"6:11::12"}, + {"text":"switch","textRange":"7:4::10","type":"KEYWORD"}, + {"text":"tag","textRange":"7:11::14"}, + {"text":"{","textRange":"7:15::16"}, + {"text":"default","textRange":"8:8::15","type":"KEYWORD"}, + {"text":":","textRange":"8:15::16"}, + {"text":"fmt","textRange":"8:17::20"}, + {"text":"Println","textRange":"8:21::28"}, + {"text":".","textRange":"8:20::21"}, + {"text":"(","textRange":"8:28::29"}, + {"text":"\"default\"","textRange":"8:29::38","type":"STRING_LITERAL"}, + {"text":")","textRange":"8:38::39"}, + {"text":"case","textRange":"9:8::12","type":"KEYWORD"}, + {"text":"0","textRange":"9:13::14"}, + {"text":"1","textRange":"9:16::17"}, + {"text":",","textRange":"9:14::15"}, + {"text":"2","textRange":"9:19::20"}, + {"text":",","textRange":"9:17::18"}, + {"text":":","textRange":"9:20::21"}, + {"text":"fmt","textRange":"9:22::25"}, + {"text":"Println","textRange":"9:26::33"}, + {"text":".","textRange":"9:25::26"}, + {"text":"(","textRange":"9:33::34"}, + {"text":"\"123\"","textRange":"9:34::39","type":"STRING_LITERAL"}, + {"text":")","textRange":"9:39::40"}, + {"text":"case","textRange":"10:8::12","type":"KEYWORD"}, + {"text":"4","textRange":"10:13::14"}, + {"text":"5","textRange":"10:16::17"}, + {"text":",","textRange":"10:14::15"}, + {"text":"6","textRange":"10:19::20"}, + {"text":",","textRange":"10:17::18"}, + {"text":"7","textRange":"10:22::23"}, + {"text":",","textRange":"10:20::21"}, + {"text":":","textRange":"10:23::24"}, + {"text":"fmt","textRange":"10:25::28"}, + {"text":"Println","textRange":"10:29::36"}, + {"text":".","textRange":"10:28::29"}, + {"text":"(","textRange":"10:36::37"}, + {"text":"\"4567\"","textRange":"10:37::43","type":"STRING_LITERAL"}, + {"text":")","textRange":"10:43::44"}, + {"text":"}","textRange":"11:4::5"}, + {"text":"x","textRange":"13:4::5"}, + {"text":":=","textRange":"13:6::8"}, + {"text":"4","textRange":"13:9::10"}, + {"text":"y","textRange":"14:4::5"}, + {"text":":=","textRange":"14:6::8"}, + {"text":"2","textRange":"14:9::10"}, + {"text":"z","textRange":"15:4::5"}, + {"text":":=","textRange":"15:6::8"}, + {"text":"3","textRange":"15:9::10"}, + {"text":"switch","textRange":"17:4::10","type":"KEYWORD"}, + {"text":"{","textRange":"17:11::12"}, + {"text":"case","textRange":"18:8::12","type":"KEYWORD"}, + {"text":"x","textRange":"18:13::14"}, + {"text":"\u003c","textRange":"18:15::16"}, + {"text":"y","textRange":"18:17::18"}, + {"text":":","textRange":"18:18::19"}, + {"text":"fmt","textRange":"18:20::23"}, + {"text":"Println","textRange":"18:24::31"}, + {"text":".","textRange":"18:23::24"}, + {"text":"(","textRange":"18:31::32"}, + {"text":"\"xy\"","textRange":"18:32::36","type":"STRING_LITERAL"}, + {"text":")","textRange":"18:36::37"}, + {"text":"case","textRange":"19:8::12","type":"KEYWORD"}, + {"text":"x","textRange":"19:13::14"}, + {"text":"\u003c","textRange":"19:15::16"}, + {"text":"z","textRange":"19:17::18"}, + {"text":":","textRange":"19:18::19"}, + {"text":"fmt","textRange":"19:20::23"}, + {"text":"Println","textRange":"19:24::31"}, + {"text":".","textRange":"19:23::24"}, + {"text":"(","textRange":"19:31::32"}, + {"text":"\"xz\"","textRange":"19:32::36","type":"STRING_LITERAL"}, + {"text":")","textRange":"19:36::37"}, + {"text":"case","textRange":"20:8::12","type":"KEYWORD"}, + {"text":"x","textRange":"20:13::14"}, + {"text":"==","textRange":"20:15::17"}, + {"text":"4","textRange":"20:18::19"}, + {"text":":","textRange":"20:19::20"}, + {"text":"fmt","textRange":"20:21::24"}, + {"text":"Println","textRange":"20:25::32"}, + {"text":".","textRange":"20:24::25"}, + {"text":"(","textRange":"20:32::33"}, + {"text":"\"x\"","textRange":"20:33::36","type":"STRING_LITERAL"}, + {"text":")","textRange":"20:36::37"}, + {"text":"}","textRange":"21:4::5"}, + {"text":"switch","textRange":"23:4::10","type":"KEYWORD"}, + {"text":"x","textRange":"23:11::12"}, + {"text":":=","textRange":"23:13::15"}, + {"text":"f","textRange":"23:16::17"}, + {"text":"(","textRange":"23:17::18"}, + {"text":")","textRange":"23:18::19"}, + {"text":"{","textRange":"23:21::22"}, + {"text":"case","textRange":"24:8::12","type":"KEYWORD"}, + {"text":"x","textRange":"24:13::14"}, + {"text":"\u003c","textRange":"24:15::16"}, + {"text":"0","textRange":"24:17::18"}, + {"text":":","textRange":"24:18::19"}, + {"text":"fmt","textRange":"24:20::23"}, + {"text":"Println","textRange":"24:24::31"}, + {"text":".","textRange":"24:23::24"}, + {"text":"(","textRange":"24:31::32"}, + {"text":"\"x\u003c0\"","textRange":"24:32::37","type":"STRING_LITERAL"}, + {"text":")","textRange":"24:37::38"}, + {"text":"default","textRange":"25:8::15","type":"KEYWORD"}, + {"text":":","textRange":"25:15::16"}, + {"text":"fmt","textRange":"25:17::20"}, + {"text":"Println","textRange":"25:21::28"}, + {"text":".","textRange":"25:20::21"}, + {"text":"(","textRange":"25:28::29"}, + {"text":"\"default\"","textRange":"25:29::38","type":"STRING_LITERAL"}, + {"text":")","textRange":"25:38::39"}, + {"text":"}","textRange":"26:4::5"}, + {"text":";","textRange":"23:19::20"}, + {"text":"var","textRange":"28:4::7","type":"KEYWORD"}, + {"text":"intType","textRange":"28:8::15"}, + {"text":"interface","textRange":"28:16::25","type":"KEYWORD"}, + {"text":"{","textRange":"28:25::26"}, + {"text":"}","textRange":"28:26::27"}, + {"text":"\"abc\"","textRange":"28:30::35","type":"STRING_LITERAL"}, + {"text":"=","textRange":"28:28::29"}, + {"text":"switch","textRange":"30:4::10","type":"KEYWORD"}, + {"text":"i","textRange":"30:11::12"}, + {"text":":=","textRange":"30:13::15"}, + {"text":"intType","textRange":"30:16::23"}, + {"text":"(","textRange":"30:24::25"}, + {"text":".","textRange":"30:23::24"}, + {"text":")","textRange":"30:29::30"}, + {"text":"type","textRange":"30:25::29","type":"KEYWORD"}, + {"text":"{","textRange":"30:31::32"}, + {"text":"case","textRange":"31:8::12","type":"KEYWORD"}, + {"text":"nil","textRange":"31:13::16"}, + {"text":":","textRange":"31:16::17"}, + {"text":"fmt","textRange":"32:12::15"}, + {"text":"Println","textRange":"32:16::23"}, + {"text":".","textRange":"32:15::16"}, + {"text":"(","textRange":"32:23::24"}, + {"text":"\"type of i is type of x (interface{})\"","textRange":"32:24::62","type":"STRING_LITERAL"}, + {"text":")","textRange":"32:62::63"}, + {"text":"break","textRange":"33:12::17","type":"KEYWORD"}, + {"text":"case","textRange":"34:8::12","type":"KEYWORD"}, + {"text":"int","textRange":"34:13::16"}, + {"text":"bool","textRange":"34:18::22"}, + {"text":",","textRange":"34:16::17"}, + {"text":":","textRange":"34:22::23"}, + {"text":"fmt","textRange":"35:12::15"}, + {"text":"Println","textRange":"35:16::23"}, + {"text":".","textRange":"35:15::16"}, + {"text":"(","textRange":"35:23::24"}, + {"text":"\"type of i is int or bool\"","textRange":"35:24::50","type":"STRING_LITERAL"}, + {"text":")","textRange":"35:50::51"}, + {"text":"case","textRange":"36:8::12","type":"KEYWORD"}, + {"text":"func","textRange":"36:13::17","type":"KEYWORD"}, + {"text":"(","textRange":"36:17::18"}, + {"text":"int","textRange":"36:18::21"}, + {"text":")","textRange":"36:21::22"}, + {"text":"float64","textRange":"36:23::30"}, + {"text":":","textRange":"36:30::31"}, + {"text":"fmt","textRange":"37:12::15"}, + {"text":"Println","textRange":"37:16::23"}, + {"text":".","textRange":"37:15::16"}, + {"text":"(","textRange":"37:23::24"}, + {"text":"\"type of i is func(int)\"","textRange":"37:24::48","type":"STRING_LITERAL"}, + {"text":")","textRange":"37:48::49"}, + {"text":"fallthrough","textRange":"38:12::23","type":"KEYWORD"}, + {"text":"case","textRange":"39:8::12","type":"KEYWORD"}, + {"text":"string","textRange":"39:13::19"}, + {"text":":","textRange":"39:19::20"}, + {"text":"{","textRange":"39:21::22"}, + {"text":"fmt","textRange":"40:16::19"}, + {"text":"Println","textRange":"40:20::27"}, + {"text":".","textRange":"40:19::20"}, + {"text":"(","textRange":"40:27::28"}, + {"text":"\"type is string and the value is \"","textRange":"40:28::62","type":"STRING_LITERAL"}, + {"text":"+","textRange":"40:63::64"}, + {"text":"i","textRange":"40:65::66"}, + {"text":")","textRange":"40:66::67"}, + {"text":"}","textRange":"41:12::13"}, + {"text":"default","textRange":"42:8::15","type":"KEYWORD"}, + {"text":":","textRange":"42:15::16"}, + {"text":"fmt","textRange":"43:12::15"}, + {"text":"Println","textRange":"43:16::23"}, + {"text":".","textRange":"43:15::16"}, + {"text":"(","textRange":"43:23::24"}, + {"text":"\"don't know the type\"","textRange":"43:24::45","type":"STRING_LITERAL"}, + {"text":")","textRange":"43:45::46"}, + {"text":"}","textRange":"44:4::5"}, + {"text":"switch","textRange":"46:8::14","type":"KEYWORD"}, + {"text":"{","textRange":"46:15::16"}, + {"text":"case","textRange":"47:12::16","type":"KEYWORD"}, + {"text":"x","textRange":"47:17::18"}, + {"text":"\u003c","textRange":"47:19::20"}, + {"text":"y","textRange":"47:21::22"}, + {"text":":","textRange":"47:22::23"}, + {"text":"fmt","textRange":"48:16::19"}, + {"text":"Println","textRange":"48:20::27"}, + {"text":".","textRange":"48:19::20"}, + {"text":"(","textRange":"48:27::28"}, + {"text":"\"xy\"","textRange":"48:28::32","type":"STRING_LITERAL"}, + {"text":")","textRange":"48:32::33"}, + {"text":"}","textRange":"49:8::9"}, + {"text":";","textRange":"48:33::34"}, + {"text":"}","textRange":"50:0::1"}, + {"text":"func","textRange":"52:0::4","type":"KEYWORD"}, + {"text":"f","textRange":"52:5::6"}, + {"text":"(","textRange":"52:6::7"}, + {"text":")","textRange":"52:7::8"}, + {"text":"int","textRange":"52:9::12"}, + {"text":"{","textRange":"52:13::14"}, + {"text":"return","textRange":"53:1::7","type":"KEYWORD"}, + {"text":"-","textRange":"53:8::9"}, + {"text":"42","textRange":"53:9::11"}, + {"text":"}","textRange":"54:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:54:1","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "ImportDeclaration", "metaData": "3:0::12","children":[ + {"@type": "Native", "metaData": "3:0::6","nativeKind":"Tok"}, + {"@type": "Native", "metaData": "3:7::12","nativeKind":"[0](ImportSpec)","children":[ + {"@type": "StringLiteral", "metaData": "3:7::12","value":"\"fmt\"","content":"fmt"} + ]} + ]}, + {"@type": "FunctionDeclaration", "metaData": "5:0:50:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "5:5::9","name":"main"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "5:12:50:1","statementOrExpressions":[ + {"@type": "VariableDeclaration", "metaData": "6:4::12","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "6:11::12","value":"1"},"identifier": + {"@type": "Identifier", "metaData": "6:4::7","name":"tag"}}, + {"@type": "Match", "metaData": "7:4:11:5","keyword":"7:4::10","expression": + {"@type": "Native", "metaData": "7:11::14","nativeKind":"InitAndTag","children":[ + {"@type": "Identifier", "metaData": "7:11::14","name":"tag"} + ]},"cases":[ + {"@type": "MatchCase", "metaData": "8:8::39","expression": + null,"body": + {"@type": "Block", "metaData": "8:17::39","statementOrExpressions":[ + {"@type": "Native", "metaData": "8:17::39","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "8:17::39","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "8:17::28","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "8:17::20","name":"fmt"}, + {"@type": "Native", "metaData": "8:20::21"}, + {"@type": "Identifier", "metaData": "8:21::28","name":"Println"} + ]}, + {"@type": "Native", "metaData": "8:28::29","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "8:29::38","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "8:29::38","value":"\"default\"","content":"default"} + ]}, + {"@type": "Native", "metaData": "8:38::39","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "9:8::40","expression": + {"@type": "Native", "metaData": "9:13::20","nativeKind":"CaseExprList","children":[ + {"@type": "IntegerLiteral", "metaData": "9:13::14","value":"0"}, + {"@type": "Native", "metaData": "9:14::15"}, + {"@type": "IntegerLiteral", "metaData": "9:16::17","value":"1"}, + {"@type": "Native", "metaData": "9:17::18"}, + {"@type": "IntegerLiteral", "metaData": "9:19::20","value":"2"} + ]},"body": + {"@type": "Block", "metaData": "9:22::40","statementOrExpressions":[ + {"@type": "Native", "metaData": "9:22::40","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "9:22::40","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "9:22::33","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "9:22::25","name":"fmt"}, + {"@type": "Native", "metaData": "9:25::26"}, + {"@type": "Identifier", "metaData": "9:26::33","name":"Println"} + ]}, + {"@type": "Native", "metaData": "9:33::34","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "9:34::39","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "9:34::39","value":"\"123\"","content":"123"} + ]}, + {"@type": "Native", "metaData": "9:39::40","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "10:8::44","expression": + {"@type": "Native", "metaData": "10:13::23","nativeKind":"CaseExprList","children":[ + {"@type": "IntegerLiteral", "metaData": "10:13::14","value":"4"}, + {"@type": "Native", "metaData": "10:14::15"}, + {"@type": "IntegerLiteral", "metaData": "10:16::17","value":"5"}, + {"@type": "Native", "metaData": "10:17::18"}, + {"@type": "IntegerLiteral", "metaData": "10:19::20","value":"6"}, + {"@type": "Native", "metaData": "10:20::21"}, + {"@type": "IntegerLiteral", "metaData": "10:22::23","value":"7"} + ]},"body": + {"@type": "Block", "metaData": "10:25::44","statementOrExpressions":[ + {"@type": "Native", "metaData": "10:25::44","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "10:25::44","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "10:25::36","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "10:25::28","name":"fmt"}, + {"@type": "Native", "metaData": "10:28::29"}, + {"@type": "Identifier", "metaData": "10:29::36","name":"Println"} + ]}, + {"@type": "Native", "metaData": "10:36::37","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "10:37::43","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "10:37::43","value":"\"4567\"","content":"4567"} + ]}, + {"@type": "Native", "metaData": "10:43::44","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]}, + {"@type": "VariableDeclaration", "metaData": "13:4::10","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "13:9::10","value":"4"},"identifier": + {"@type": "Identifier", "metaData": "13:4::5","name":"x"}}, + {"@type": "VariableDeclaration", "metaData": "14:4::10","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "14:9::10","value":"2"},"identifier": + {"@type": "Identifier", "metaData": "14:4::5","name":"y"}}, + {"@type": "VariableDeclaration", "metaData": "15:4::10","type":null,"isVal":false,"initializer": + {"@type": "IntegerLiteral", "metaData": "15:9::10","value":"3"},"identifier": + {"@type": "Identifier", "metaData": "15:4::5","name":"z"}}, + {"@type": "Match", "metaData": "17:4:21:5","keyword":"17:4::10","expression": + null,"cases":[ + {"@type": "MatchCase", "metaData": "18:8::37","expression": + {"@type": "Native", "metaData": "18:13::18","nativeKind":"CaseExprList","children":[ + {"@type": "BinaryExpression", "metaData": "18:13::18","rightOperand": + {"@type": "Identifier", "metaData": "18:17::18","name":"y"},"operatorToken":"18:15::16","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "18:13::14","name":"x"}} + ]},"body": + {"@type": "Block", "metaData": "18:20::37","statementOrExpressions":[ + {"@type": "Native", "metaData": "18:20::37","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "18:20::37","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "18:20::31","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "18:20::23","name":"fmt"}, + {"@type": "Native", "metaData": "18:23::24"}, + {"@type": "Identifier", "metaData": "18:24::31","name":"Println"} + ]}, + {"@type": "Native", "metaData": "18:31::32","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "18:32::36","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "18:32::36","value":"\"xy\"","content":"xy"} + ]}, + {"@type": "Native", "metaData": "18:36::37","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "19:8::37","expression": + {"@type": "Native", "metaData": "19:13::18","nativeKind":"CaseExprList","children":[ + {"@type": "BinaryExpression", "metaData": "19:13::18","rightOperand": + {"@type": "Identifier", "metaData": "19:17::18","name":"z"},"operatorToken":"19:15::16","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "19:13::14","name":"x"}} + ]},"body": + {"@type": "Block", "metaData": "19:20::37","statementOrExpressions":[ + {"@type": "Native", "metaData": "19:20::37","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "19:20::37","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "19:20::31","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "19:20::23","name":"fmt"}, + {"@type": "Native", "metaData": "19:23::24"}, + {"@type": "Identifier", "metaData": "19:24::31","name":"Println"} + ]}, + {"@type": "Native", "metaData": "19:31::32","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "19:32::36","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "19:32::36","value":"\"xz\"","content":"xz"} + ]}, + {"@type": "Native", "metaData": "19:36::37","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "20:8::37","expression": + {"@type": "Native", "metaData": "20:13::19","nativeKind":"CaseExprList","children":[ + {"@type": "BinaryExpression", "metaData": "20:13::19","rightOperand": + {"@type": "IntegerLiteral", "metaData": "20:18::19","value":"4"},"operatorToken":"20:15::17","operator":"EQUAL_TO","leftOperand": + {"@type": "Identifier", "metaData": "20:13::14","name":"x"}} + ]},"body": + {"@type": "Block", "metaData": "20:21::37","statementOrExpressions":[ + {"@type": "Native", "metaData": "20:21::37","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "20:21::37","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "20:21::32","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "20:21::24","name":"fmt"}, + {"@type": "Native", "metaData": "20:24::25"}, + {"@type": "Identifier", "metaData": "20:25::32","name":"Println"} + ]}, + {"@type": "Native", "metaData": "20:32::33","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "20:33::36","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "20:33::36","value":"\"x\"","content":"x"} + ]}, + {"@type": "Native", "metaData": "20:36::37","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]}, + {"@type": "Match", "metaData": "23:4:26:5","keyword":"23:4::10","expression": + {"@type": "Native", "metaData": "23:11::19","nativeKind":"InitAndTag","children":[ + {"@type": "VariableDeclaration", "metaData": "23:11::19","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "23:16::19","nativeKind":"[0](CallExpr)","children":[ + {"@type": "Identifier", "metaData": "23:16::17","name":"f"}, + {"@type": "Native", "metaData": "23:17::18","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "23:18::19","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "23:11::12","name":"x"}} + ]},"cases":[ + {"@type": "MatchCase", "metaData": "24:8::38","expression": + {"@type": "Native", "metaData": "24:13::18","nativeKind":"CaseExprList","children":[ + {"@type": "BinaryExpression", "metaData": "24:13::18","rightOperand": + {"@type": "IntegerLiteral", "metaData": "24:17::18","value":"0"},"operatorToken":"24:15::16","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "24:13::14","name":"x"}} + ]},"body": + {"@type": "Block", "metaData": "24:20::38","statementOrExpressions":[ + {"@type": "Native", "metaData": "24:20::38","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "24:20::38","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "24:20::31","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "24:20::23","name":"fmt"}, + {"@type": "Native", "metaData": "24:23::24"}, + {"@type": "Identifier", "metaData": "24:24::31","name":"Println"} + ]}, + {"@type": "Native", "metaData": "24:31::32","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "24:32::37","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "24:32::37","value":"\"x\u003c0\"","content":"x\u003c0"} + ]}, + {"@type": "Native", "metaData": "24:37::38","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "25:8::39","expression": + null,"body": + {"@type": "Block", "metaData": "25:17::39","statementOrExpressions":[ + {"@type": "Native", "metaData": "25:17::39","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "25:17::39","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "25:17::28","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "25:17::20","name":"fmt"}, + {"@type": "Native", "metaData": "25:20::21"}, + {"@type": "Identifier", "metaData": "25:21::28","name":"Println"} + ]}, + {"@type": "Native", "metaData": "25:28::29","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "25:29::38","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "25:29::38","value":"\"default\"","content":"default"} + ]}, + {"@type": "Native", "metaData": "25:38::39","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]}, + {"@type": "Native", "metaData": "28:4::35","nativeKind":"[7](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "28:4::35","type": + {"@type": "Native", "metaData": "28:16::27","nativeKind":"Type(InterfaceType)","children":[ + {"@type": "Native", "metaData": "28:16::25","nativeKind":"Interface"}, + {"@type": "Native", "metaData": "28:25::27","nativeKind":"Methods(FieldList)","children":[ + {"@type": "Native", "metaData": "28:25::26","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "28:26::27","nativeKind":"Closing"} + ]} + ]},"isVal":false,"initializer": + {"@type": "StringLiteral", "metaData": "28:30::35","value":"\"abc\"","content":"abc"},"identifier": + {"@type": "Identifier", "metaData": "28:8::15","name":"intType"}} + ]}, + {"@type": "Match", "metaData": "30:4:44:5","keyword":"30:4::10","expression": + {"@type": "Native", "metaData": "30:11::30","nativeKind":"InitAndAssign","children":[ + {"@type": "VariableDeclaration", "metaData": "30:11::30","type":null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "30:16::30","nativeKind":"[0](TypeAssertExpr)","children":[ + {"@type": "Identifier", "metaData": "30:16::23","name":"intType"}, + {"@type": "Native", "metaData": "30:23::24"}, + {"@type": "Native", "metaData": "30:24::25","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "30:25::29"}, + {"@type": "Native", "metaData": "30:29::30","nativeKind":"Rparen"} + ]},"identifier": + {"@type": "Identifier", "metaData": "30:11::12","name":"i"}} + ]},"cases":[ + {"@type": "MatchCase", "metaData": "31:8:33:17","expression": + {"@type": "Native", "metaData": "31:13::16","nativeKind":"CaseExprList","children":[ + {"@type": "Literal", "metaData": "31:13::16","value":"nil"} + ]},"body": + {"@type": "Block", "metaData": "32:12:33:17","statementOrExpressions":[ + {"@type": "Native", "metaData": "32:12::63","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "32:12::63","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "32:12::23","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "32:12::15","name":"fmt"}, + {"@type": "Native", "metaData": "32:15::16"}, + {"@type": "Identifier", "metaData": "32:16::23","name":"Println"} + ]}, + {"@type": "Native", "metaData": "32:23::24","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "32:24::62","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "32:24::62","value":"\"type of i is type of x (interface{})\"","content":"type of i is type of x (interface{})"} + ]}, + {"@type": "Native", "metaData": "32:62::63","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Jump", "metaData": "33:12::17","label": + null,"kind":"BREAK","keyword":"33:12::17"} + ]}}, + {"@type": "MatchCase", "metaData": "34:8:35:51","expression": + {"@type": "Native", "metaData": "34:13::22","nativeKind":"CaseExprList","children":[ + {"@type": "Identifier", "metaData": "34:13::16","name":"int"}, + {"@type": "Native", "metaData": "34:16::17"}, + {"@type": "Identifier", "metaData": "34:18::22","name":"bool"} + ]},"body": + {"@type": "Block", "metaData": "35:12::51","statementOrExpressions":[ + {"@type": "Native", "metaData": "35:12::51","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "35:12::51","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "35:12::23","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "35:12::15","name":"fmt"}, + {"@type": "Native", "metaData": "35:15::16"}, + {"@type": "Identifier", "metaData": "35:16::23","name":"Println"} + ]}, + {"@type": "Native", "metaData": "35:23::24","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "35:24::50","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "35:24::50","value":"\"type of i is int or bool\"","content":"type of i is int or bool"} + ]}, + {"@type": "Native", "metaData": "35:50::51","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "36:8:38:23","expression": + {"@type": "Native", "metaData": "36:13::30","nativeKind":"CaseExprList","children":[ + {"@type": "Native", "metaData": "36:13::30","nativeKind":"[0](FuncType)","children":[ + {"@type": "Native", "metaData": "36:13::17","nativeKind":"Func"}, + {"@type": "Native", "metaData": "36:17::22","nativeKind":"Params(FieldList)","children":[ + {"@type": "Native", "metaData": "36:17::18","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "36:18::21","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "36:18::21","name":"int"} + ]}, + {"@type": "Native", "metaData": "36:21::22","nativeKind":"Closing"} + ]}, + {"@type": "Native", "metaData": "36:23::30","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "36:23::30","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "36:23::30","name":"float64"} + ]} + ]} + ]} + ]},"body": + {"@type": "Block", "metaData": "37:12:38:23","statementOrExpressions":[ + {"@type": "Native", "metaData": "37:12::49","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "37:12::49","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "37:12::23","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "37:12::15","name":"fmt"}, + {"@type": "Native", "metaData": "37:15::16"}, + {"@type": "Identifier", "metaData": "37:16::23","name":"Println"} + ]}, + {"@type": "Native", "metaData": "37:23::24","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "37:24::48","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "37:24::48","value":"\"type of i is func(int)\"","content":"type of i is func(int)"} + ]}, + {"@type": "Native", "metaData": "37:48::49","nativeKind":"Rparen"} + ]} + ]}, + {"@type": "Native", "metaData": "38:12::23","nativeKind":"[1](BranchStmt)","children":[ + {"@type": "Native", "metaData": "38:12::23","nativeKind":"Tok"} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "39:8:41:13","expression": + {"@type": "Native", "metaData": "39:13::19","nativeKind":"CaseExprList","children":[ + {"@type": "Identifier", "metaData": "39:13::19","name":"string"} + ]},"body": + {"@type": "Block", "metaData": "39:21:41:13","statementOrExpressions":[ + {"@type": "Native", "metaData": "40:16::67","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "40:16::67","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "40:16::27","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "40:16::19","name":"fmt"}, + {"@type": "Native", "metaData": "40:19::20"}, + {"@type": "Identifier", "metaData": "40:20::27","name":"Println"} + ]}, + {"@type": "Native", "metaData": "40:27::28","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "40:28::66","nativeKind":"Args([]Expr)","children":[ + {"@type": "BinaryExpression", "metaData": "40:28::66","rightOperand": + {"@type": "Identifier", "metaData": "40:65::66","name":"i"},"operatorToken":"40:63::64","operator":"PLUS","leftOperand": + {"@type": "StringLiteral", "metaData": "40:28::62","value":"\"type is string and the value is \"","content":"type is string and the value is "}} + ]}, + {"@type": "Native", "metaData": "40:66::67","nativeKind":"Rparen"} + ]} + ]} + ]}}, + {"@type": "MatchCase", "metaData": "42:8:43:46","expression": + null,"body": + {"@type": "Block", "metaData": "43:12::46","statementOrExpressions":[ + {"@type": "Native", "metaData": "43:12::46","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "43:12::46","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "43:12::23","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "43:12::15","name":"fmt"}, + {"@type": "Native", "metaData": "43:15::16"}, + {"@type": "Identifier", "metaData": "43:16::23","name":"Println"} + ]}, + {"@type": "Native", "metaData": "43:23::24","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "43:24::45","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "43:24::45","value":"\"don't know the type\"","content":"don't know the type"} + ]}, + {"@type": "Native", "metaData": "43:45::46","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]}, + {"@type": "Match", "metaData": "46:8:49:9","keyword":"46:8::14","expression": + null,"cases":[ + {"@type": "MatchCase", "metaData": "47:12:48:33","expression": + {"@type": "Native", "metaData": "47:17::22","nativeKind":"CaseExprList","children":[ + {"@type": "BinaryExpression", "metaData": "47:17::22","rightOperand": + {"@type": "Identifier", "metaData": "47:21::22","name":"y"},"operatorToken":"47:19::20","operator":"LESS_THAN","leftOperand": + {"@type": "Identifier", "metaData": "47:17::18","name":"x"}} + ]},"body": + {"@type": "Block", "metaData": "48:16::33","statementOrExpressions":[ + {"@type": "Native", "metaData": "48:16::33","nativeKind":"[0](ExprStmt)","children":[ + {"@type": "Native", "metaData": "48:16::33","nativeKind":"X(CallExpr)","children":[ + {"@type": "Native", "metaData": "48:16::27","nativeKind":"Fun(SelectorExpr)","children":[ + {"@type": "Identifier", "metaData": "48:16::19","name":"fmt"}, + {"@type": "Native", "metaData": "48:19::20"}, + {"@type": "Identifier", "metaData": "48:20::27","name":"Println"} + ]}, + {"@type": "Native", "metaData": "48:27::28","nativeKind":"Lparen"}, + {"@type": "Native", "metaData": "48:28::32","nativeKind":"Args([]Expr)","children":[ + {"@type": "StringLiteral", "metaData": "48:28::32","value":"\"xy\"","content":"xy"} + ]}, + {"@type": "Native", "metaData": "48:32::33","nativeKind":"Rparen"} + ]} + ]} + ]}} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "52:0:54:1","returnType": + {"@type": "Native", "metaData": "52:9::12","nativeKind":"Results(FieldList)","children":[ + {"@type": "Native", "metaData": "52:9::12","nativeKind":"[0](Field)","children":[ + {"@type": "Identifier", "metaData": "52:9::12","name":"int"} + ]} + ]},"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "52:5::6","name":"f"},"modifiers":null,"isConstructor":false,"formalParameters":[],"body": + {"@type": "Block", "metaData": "52:13:54:1","statementOrExpressions":[ + {"@type": "Return", "metaData": "53:1::11","keyword":"53:1::7","body": + {"@type": "UnaryExpression", "metaData": "53:8::11","operator":"MINUS","operand": + {"@type": "IntegerLiteral", "metaData": "53:9::11","value":"42"}}} + ]}} + ]} +} diff --git a/sonar-go-to-slang/resources/ast/unary.go.source b/sonar-go-to-slang/resources/ast/unary.go.source new file mode 100644 index 00000000..7b561c60 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/unary.go.source @@ -0,0 +1,25 @@ +package main + +type Person struct { + name string + age int +} + +/* +https://golang.org/ref/spec#unary_op +unary_op = "+" | "-" | "!" | "^" | "*" | "&" | "<-" +*/ +func foo(value *Person) { + var x = +1 + var y = -1 + var c = ^4 + + // increment and decrement are not operators, but statements + x++ + x-- + + var b = !true + + var p = *value + var a = &value +} diff --git a/sonar-go-to-slang/resources/ast/unary.json b/sonar-go-to-slang/resources/ast/unary.json new file mode 100644 index 00000000..478719c1 --- /dev/null +++ b/sonar-go-to-slang/resources/ast/unary.json @@ -0,0 +1,159 @@ +{ + "treeMetaData": { + "comments": [ + {"text":"/*\nhttps://golang.org/ref/spec#unary_op\nunary_op = \"+\" | \"-\" | \"!\" | \"^\" | \"*\" | \"\u0026\" | \"\u003c-\"\n*/", "contentText":"\nhttps://golang.org/ref/spec#unary_op\nunary_op = \"+\" | \"-\" | \"!\" | \"^\" | \"*\" | \"\u0026\" | \"\u003c-\"\n", "range":"8:0:11:2", "contentRange": "8:2:11:0"}, + {"text":"// increment and decrement are not operators, but statements", "contentText":" increment and decrement are not operators, but statements", "range":"17:1::61", "contentRange": "17:3::61"} + ], + "tokens": [ + {"text":"package","textRange":"1:0::7","type":"KEYWORD"}, + {"text":"main","textRange":"1:8::12"}, + {"text":"type","textRange":"3:0::4","type":"KEYWORD"}, + {"text":"Person","textRange":"3:5::11"}, + {"text":"struct","textRange":"3:12::18","type":"KEYWORD"}, + {"text":"{","textRange":"3:19::20"}, + {"text":"name","textRange":"4:1::5"}, + {"text":"string","textRange":"4:6::12"}, + {"text":"age","textRange":"5:1::4"}, + {"text":"int","textRange":"5:6::9"}, + {"text":"}","textRange":"6:0::1"}, + {"text":"func","textRange":"12:0::4","type":"KEYWORD"}, + {"text":"foo","textRange":"12:5::8"}, + {"text":"(","textRange":"12:8::9"}, + {"text":"value","textRange":"12:9::14"}, + {"text":"*","textRange":"12:15::16"}, + {"text":"Person","textRange":"12:16::22"}, + {"text":")","textRange":"12:22::23"}, + {"text":"{","textRange":"12:24::25"}, + {"text":"var","textRange":"13:1::4","type":"KEYWORD"}, + {"text":"x","textRange":"13:5::6"}, + {"text":"+","textRange":"13:9::10"}, + {"text":"1","textRange":"13:10::11"}, + {"text":"=","textRange":"13:7::8"}, + {"text":"var","textRange":"14:1::4","type":"KEYWORD"}, + {"text":"y","textRange":"14:5::6"}, + {"text":"-","textRange":"14:9::10"}, + {"text":"1","textRange":"14:10::11"}, + {"text":"=","textRange":"14:7::8"}, + {"text":"var","textRange":"15:1::4","type":"KEYWORD"}, + {"text":"c","textRange":"15:5::6"}, + {"text":"^","textRange":"15:9::10"}, + {"text":"4","textRange":"15:10::11"}, + {"text":"=","textRange":"15:7::8"}, + {"text":"x","textRange":"18:1::2"}, + {"text":"++","textRange":"18:2::4"}, + {"text":"x","textRange":"19:1::2"}, + {"text":"--","textRange":"19:2::4"}, + {"text":"var","textRange":"21:1::4","type":"KEYWORD"}, + {"text":"b","textRange":"21:5::6"}, + {"text":"!","textRange":"21:9::10"}, + {"text":"true","textRange":"21:10::14"}, + {"text":"=","textRange":"21:7::8"}, + {"text":"var","textRange":"23:1::4","type":"KEYWORD"}, + {"text":"p","textRange":"23:5::6"}, + {"text":"*","textRange":"23:9::10"}, + {"text":"value","textRange":"23:10::15"}, + {"text":"=","textRange":"23:7::8"}, + {"text":"var","textRange":"24:1::4","type":"KEYWORD"}, + {"text":"a","textRange":"24:5::6"}, + {"text":"\u0026","textRange":"24:9::10"}, + {"text":"value","textRange":"24:10::15"}, + {"text":"=","textRange":"24:7::8"}, + {"text":"}","textRange":"25:0::1"} + ] + }, + "tree": + {"@type": "TopLevel", "metaData": "1:0:26:0","firstCpdToken":null,"declarations":[ + {"@type": "PackageDeclaration", "metaData": "1:0::12","children":[ + {"@type": "Native", "metaData": "1:0::7"}, + {"@type": "Identifier", "metaData": "1:8::12","name":"main"} + ]}, + {"@type": "ClassDeclaration", "metaData": "3:0:6:1","identifier":"3:5::11","classTree": + {"@type": "Native", "metaData": "3:0:6:1","nativeKind":"[0](TypeSpecWrapped)","children":[ + {"@type": "Native", "metaData": "3:0::4","nativeKind":"Tok"}, + {"@type": "Identifier", "metaData": "3:5::11","name":"Person"}, + {"@type": "Native", "metaData": "3:12:6:1","nativeKind":"Type(StructType)","children":[ + {"@type": "Native", "metaData": "3:12::18","nativeKind":"Struct"}, + {"@type": "Native", "metaData": "3:19:6:1","nativeKind":"Fields(FieldList)","children":[ + {"@type": "Native", "metaData": "3:19::20","nativeKind":"Opening"}, + {"@type": "Native", "metaData": "4:1::12","nativeKind":"[0](Field)","children":[ + {"@type": "Native", "metaData": "4:1::5","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "4:1::5","name":"name"} + ]}, + {"@type": "Identifier", "metaData": "4:6::12","name":"string"} + ]}, + {"@type": "Native", "metaData": "5:1::9","nativeKind":"[1](Field)","children":[ + {"@type": "Native", "metaData": "5:1::4","nativeKind":"Names([]*Ident)","children":[ + {"@type": "Identifier", "metaData": "5:1::4","name":"age"} + ]}, + {"@type": "Identifier", "metaData": "5:6::9","name":"int"} + ]}, + {"@type": "Native", "metaData": "6:0::1","nativeKind":"Closing"} + ]} + ]} + ]}}, + {"@type": "FunctionDeclaration", "metaData": "12:0:25:1","returnType": + null,"nativeChildren":[],"name": + {"@type": "Identifier", "metaData": "12:5::8","name":"foo"},"modifiers":null,"isConstructor":false,"formalParameters":[ + {"@type": "Parameter", "metaData": "12:9::22","type": + {"@type": "Native", "metaData": "12:15::22","nativeKind":"Type(StarExpr)","children":[ + {"@type": "Native", "metaData": "12:15::16","nativeKind":"Star"}, + {"@type": "Identifier", "metaData": "12:16::22","name":"Person"} + ]},"modifiers":null,"identifier": + {"@type": "Identifier", "metaData": "12:9::14","name":"value"},"defaultValue":null} + ],"body": + {"@type": "Block", "metaData": "12:24:25:1","statementOrExpressions":[ + {"@type": "Native", "metaData": "13:1::11","nativeKind":"[0](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "13:1::11","type": + null,"isVal":false,"initializer": + {"@type": "UnaryExpression", "metaData": "13:9::11","operator":"PLUS","operand": + {"@type": "IntegerLiteral", "metaData": "13:10::11","value":"1"}},"identifier": + {"@type": "Identifier", "metaData": "13:5::6","name":"x"}} + ]}, + {"@type": "Native", "metaData": "14:1::11","nativeKind":"[1](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "14:1::11","type": + null,"isVal":false,"initializer": + {"@type": "UnaryExpression", "metaData": "14:9::11","operator":"MINUS","operand": + {"@type": "IntegerLiteral", "metaData": "14:10::11","value":"1"}},"identifier": + {"@type": "Identifier", "metaData": "14:5::6","name":"y"}} + ]}, + {"@type": "Native", "metaData": "15:1::11","nativeKind":"[2](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "15:1::11","type": + null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "15:9::11","nativeKind":"[0](UnaryExpr)","children":[ + {"@type": "Native", "metaData": "15:9::10","nativeKind":"Op"}, + {"@type": "IntegerLiteral", "metaData": "15:10::11","value":"4"} + ]},"identifier": + {"@type": "Identifier", "metaData": "15:5::6","name":"c"}} + ]}, + {"@type": "UnaryExpression", "metaData": "18:1::4","operator":"INCREMENT","operand": + {"@type": "Identifier", "metaData": "18:1::2","name":"x"}}, + {"@type": "UnaryExpression", "metaData": "19:1::4","operator":"DECREMENT","operand": + {"@type": "Identifier", "metaData": "19:1::2","name":"x"}}, + {"@type": "Native", "metaData": "21:1::14","nativeKind":"[5](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "21:1::14","type": + null,"isVal":false,"initializer": + {"@type": "UnaryExpression", "metaData": "21:9::14","operator":"NEGATE","operand": + {"@type": "Literal", "metaData": "21:10::14","value":"true"}},"identifier": + {"@type": "Identifier", "metaData": "21:5::6","name":"b"}} + ]}, + {"@type": "Native", "metaData": "23:1::15","nativeKind":"[6](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "23:1::15","type": + null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "23:9::15","nativeKind":"[0](StarExpr)","children":[ + {"@type": "Native", "metaData": "23:9::10","nativeKind":"Star"}, + {"@type": "Identifier", "metaData": "23:10::15","name":"value"} + ]},"identifier": + {"@type": "Identifier", "metaData": "23:5::6","name":"p"}} + ]}, + {"@type": "Native", "metaData": "24:1::15","nativeKind":"[7](DeclStmt)","children":[ + {"@type": "VariableDeclaration", "metaData": "24:1::15","type": + null,"isVal":false,"initializer": + {"@type": "Native", "metaData": "24:9::15","nativeKind":"[0](UnaryExpr)","children":[ + {"@type": "Native", "metaData": "24:9::10","nativeKind":"Op"}, + {"@type": "Identifier", "metaData": "24:10::15","name":"value"} + ]},"identifier": + {"@type": "Identifier", "metaData": "24:5::6","name":"a"}} + ]} + ]}} + ]} +} diff --git a/wss-unified-agent.config b/wss-unified-agent.config new file mode 100644 index 00000000..751f56d8 --- /dev/null +++ b/wss-unified-agent.config @@ -0,0 +1,20 @@ +excludes=**/its/sources/** **/*javadoc.jar +fileSystemScan=False +resolveAllDependencies=False + +gradle.aggregateModules=True +gradle.preferredEnvironment=wrapper +gradle.resolveDependencies=True + +maven.aggregateModules=False +maven.downloadMissingDependencies=False +maven.ignoredScopes=None +maven.m2RepositoryPath=.m2/repository +maven.resolveDependencies=False +maven.runPreStep=False + +wss.url=https://saas-eu.whitesourcesoftware.com/agent + +forceUpdate=true +checkPolicies=true +forceUpdate.failBuildOnPolicyViolation=true