All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sourcegraph.scip_semanticdb.ScipTextDocument Maven / Gradle / Ivy

package com.sourcegraph.scip_semanticdb;

import com.sourcegraph.semanticdb_javac.Semanticdb;
import com.sourcegraph.semanticdb_javac.SemanticdbSymbols;

import java.nio.file.Path;
import java.util.*;

/** Wrapper around a SemanticDB TextDocument with SCIP-related utilities. */
public class ScipTextDocument {
  public final Path semanticdbPath;
  public Semanticdb.TextDocument semanticdb;
  public int id;
  public final Map symbols;
  public final Map localSymbols;
  // Map from symbols that have a definition occurrence to the list of symbols that have
  // `is_definition` relationships to that symbol.
  // This map is used to add `is_reference` relationships between all symbols in the list so that
  // doing "Find references"
  // on any of those symbols returns occurrences for all of the symbols in the "clique" (per
  // https://en.wikipedia.org/wiki/Clique_(graph_theory)).
  // See https://github.com/sourcegraph/sourcegraph/issues/50927 for more details.
  public final Map> definitionCliques = new HashMap<>();

  public ScipTextDocument(
      Path semanticdbPath, Semanticdb.TextDocument semanticdb, Path sourceroot) {
    this.semanticdbPath = semanticdbPath;
    this.symbols = new HashMap<>();
    this.localSymbols = new HashMap<>();
    String uri = sourceroot.resolve(semanticdb.getUri()).toUri().toString();
    setSemanticdb(Semanticdb.TextDocument.newBuilder(semanticdb).setUri(uri).build());
  }

  @Override
  public String toString() {
    return "ScipDocument{"
        + "path="
        + semanticdbPath
        + ", semanticdb="
        + semanticdb
        + ", id="
        + id
        + '}';
  }

  public List sortedSymbolOccurrences() {
    return ScipTextDocument.sortedSymbolOccurrences(semanticdb);
  }

  public static List sortedSymbolOccurrences(
      Semanticdb.TextDocument semanticdb) {
    ArrayList result =
        new ArrayList<>(semanticdb.getOccurrencesList().size());
    result.addAll(semanticdb.getOccurrencesList());
    for (Semanticdb.Synthetic synthetic : semanticdb.getSyntheticsList()) {
      addAllSyntheticOccurrences(synthetic, result);
    }
    result.sort((o1, o2) -> new RangeComparator().compare(o1.getRange(), o2.getRange()));
    return result;
  }

  private static void addAllSyntheticOccurrences(
      Semanticdb.Synthetic synthetic, ArrayList buffer) {
    Semanticdb.Range offsetRange =
        Semanticdb.Range.newBuilder(synthetic.getRange())
            .setStartLine(synthetic.getRange().getEndLine())
            .setStartCharacter(synthetic.getRange().getEndCharacter())
            .build();
    new SemanticdbTreeVisitor() {
      @Override
      void visitIdTree(Semanticdb.IdTree tree) {
        Semanticdb.SymbolOccurrence syntheticOccurrence =
            Semanticdb.SymbolOccurrence.newBuilder()
                .setRange(offsetRange)
                .setSymbol(tree.getSymbol())
                .setRole(Semanticdb.SymbolOccurrence.Role.REFERENCE)
                .build();
        buffer.add(syntheticOccurrence);
      }
    }.visitTree(synthetic.getTree());
  }

  private void setSemanticdb(Semanticdb.TextDocument semanticdb) {
    this.semanticdb = manifestOccurrencesForSyntheticSymbols(semanticdb);
    for (Semanticdb.SymbolInformation info : semanticdb.getSymbolsList()) {
      symbols.put(info.getSymbol(), info);
    }
  }

  public Semanticdb.TextDocument manifestOccurrencesForSyntheticSymbols(
      Semanticdb.TextDocument semanticdb) {
    if (semanticdb.getLanguage() != Semanticdb.Language.SCALA) {
      // It's only semanticdb-scalac that emits SymbolInformation for symbols that have no
      // definition occurrences.
      return semanticdb;
    }
    Semanticdb.TextDocument.Builder builder = Semanticdb.TextDocument.newBuilder(semanticdb);
    builder.clearSymbols();
    HashMap definitionOccurrences = new HashMap<>();
    for (Semanticdb.SymbolOccurrence occ : semanticdb.getOccurrencesList()) {
      if (occ.getRole() == Semanticdb.SymbolOccurrence.Role.DEFINITION) {
        definitionOccurrences.put(occ.getSymbol(), occ);
      }
    }
    for (Semanticdb.SymbolInformation info : semanticdb.getSymbolsList()) {
      Semanticdb.SymbolInformation.Builder newInfo = Semanticdb.SymbolInformation.newBuilder(info);
      Semanticdb.SymbolOccurrence definition = definitionOccurrences.get(info.getSymbol());
      if (definition != null) {
        // This symbol has a definition so it doesn't need an is_definition relationship.
        builder.addSymbols(newInfo);
        continue;
      }
      for (Semanticdb.SymbolOccurrence alternativeSymbol : alternativeSymbols(info)) {
        Semanticdb.SymbolOccurrence alternativeDefinition =
            definitionOccurrences.get(alternativeSymbol.getSymbol());
        if (alternativeDefinition != null) {
          ArrayList clique =
              this.definitionCliques.computeIfAbsent(
                  alternativeSymbol.getSymbol(), k -> new ArrayList<>());
          clique.add(info.getSymbol());
          newInfo.addDefinitionRelationships(alternativeDefinition.getSymbol());
          break;
        }
      }
      builder.addSymbols(newInfo);
    }
    return builder.build();
  }

  public static final Set syntheticCaseClassMethodNames =
      new HashSet<>(Arrays.asList("copy", "productElement", "productElementName"));
  public static final Set syntheticCompanionObjectNames =
      new HashSet<>(Arrays.asList("apply", "copy"));

  public static List alternativeSymbols(
      Semanticdb.SymbolInformation info) {
    SymbolOccurrences alternatives = new SymbolOccurrences();
    SymbolDescriptor sym = SymbolDescriptor.parseFromSymbol(info.getSymbol());
    switch (sym.descriptor.kind) {
      case Method:
        if (sym.descriptor.name.endsWith("_=")) {
          String newName = sym.descriptor.name.substring(0, sym.descriptor.name.length() - 2);
          alternatives.addDefinition(
              SemanticdbSymbols.global(sym.owner, sym.descriptor.withName(newName)));
        } else if (syntheticCaseClassMethodNames.contains(sym.descriptor.name)) {
          alternatives.addSyntheticDefinition(sym.owner);
        } else if (syntheticCompanionObjectNames.contains(sym.descriptor.name)) {
          alternatives.addSyntheticDefinition(sym.owner);
          SymbolDescriptor owner = SymbolDescriptor.parseFromSymbol(sym.owner);
          alternatives.addSyntheticDefinition(
              SemanticdbSymbols.global(
                  owner.owner, owner.descriptor.withKind(SemanticdbSymbols.Descriptor.Kind.Type)));
        }
        break;
      case Parameter:
        SymbolDescriptor owner = SymbolDescriptor.parseFromSymbol(sym.owner);
        if (owner.descriptor.name.equals("copy") || owner.descriptor.name.equals("")) {
          // case classes copy method parameter.
          alternatives.addDefinition(
              SemanticdbSymbols.global(
                  owner.owner, sym.descriptor.withKind(SemanticdbSymbols.Descriptor.Kind.Term)));
        } else if (owner.descriptor.name.equals("apply")) {
          // case class companion apply constructor parameter.
          SymbolDescriptor grandparent = SymbolDescriptor.parseFromSymbol(owner.owner);
          String companion =
              SemanticdbSymbols.global(
                  grandparent.owner,
                  grandparent.descriptor.withKind(SemanticdbSymbols.Descriptor.Kind.Type));
          alternatives.addDefinition(
              SemanticdbSymbols.global(
                  companion, sym.descriptor.withKind(SemanticdbSymbols.Descriptor.Kind.Term)));
        }
      case Term:
        alternatives.addDefinition(
            SemanticdbSymbols.global(
                sym.owner, sym.descriptor.withKind(SemanticdbSymbols.Descriptor.Kind.Type)));
        break;
      default:
    }

    return alternatives.occurrences;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy