Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/codeql/codeql-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#using-a-custom-configuration-file
name: "CodeQL config"
queries:
- uses: security-and-quality
- uses: ./.github/codeql/queries/java # load our custom CodeQL rules

query-filters:
- exclude:
# Exclude the built-in shadowing rule.
# We intentionally use final locals that copy instance fields
# (e.g. `final var name = this.name`) to support Eclipse null analysis
# without introducing noisy renaming. This pattern is deliberate and safe,
# so the built-in rule is disabled in favor of our custom rule
# (local-shadows-instance-field.ql).
id: java/local-shadows-field
47 changes: 47 additions & 0 deletions .github/codeql/queries/java/local-shadows-instance-field.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* @name Local variable shadows instance field (except final this-copy)
* @description Flags local variables that shadow an instance field, unless they are final
* and initialized directly from that same field (for example `final var name = this.name;`).
* @id custom-java/local-shadows-instance-field-strict
* @kind problem
* @problem.severity warning
* @precision medium
* @tags correctness readability maintainability
*/
import java
import semmle.code.java.Member
import semmle.code.java.Variable

/**
* True if this local variable is an allowed shadowing of the given instance field:
*
* final var name = this.name;
* final var name = name; // implicit this
*/
predicate allowedShadowing(LocalVariableDecl local, Field field) {
local.isFinal() and
exists(FieldAccess fa |
fa = local.getInitializer().getUnderlyingExpr().(FieldAccess) and
fa.getField() = field and
fa.isOwnFieldAccess()
)
}

from LocalVariableDecl local, Field field, Callable c
where
// same simple name
local.getName() = field.getName() and

// only consider instance fields
not field.isStatic() and

// local is inside a callable of a class related to the field's declaring type
c = local.getCallable() and
c.getDeclaringType().getASupertype*() = field.getDeclaringType() and

// shadowing is NOT in the allowed final-this-copy form
not allowedShadowing(local, field)
select local,
"Local variable '" + local.getName() + "' shadows instance field '" +
field.getQualifiedName() +
"'. Shadowing is only allowed for 'final' locals directly initialized from this field."
2 changes: 1 addition & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ jobs:
# https://github.com/github/codeql-action#build-modes
build-mode: ${{ matrix.build-mode }}
# https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning#using-queries-in-ql-packs
queries: +security-and-quality
config-file: ./.github/codeql/codeql-config.yml


- name: "Build with Maven 🔨"
Expand Down
7 changes: 5 additions & 2 deletions org.eclipse.lsp4e.debug/META-INF/MANIFEST.MF
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ Require-Bundle: org.eclipse.ui,
org.eclipse.jface.text,
org.eclipse.ui.genericeditor,
org.eclipse.core.variables,
org.eclipse.lsp4e;bundle-version="0.18.13"
org.eclipse.lsp4e;bundle-version="0.19.0",
org.eclipse.tm4e.core;resolution:=optional,
org.eclipse.tm4e.registry;resolution:=optional,
org.eclipse.tm4e.ui;resolution:=optional
Bundle-RequiredExecutionEnvironment: JavaSE-21
Bundle-ActivationPolicy: lazy
Import-Package: com.google.gson,
com.google.gson.reflect;version="2.7.0"
Export-Package: org.eclipse.lsp4e.debug;x-internal:=true,
org.eclipse.lsp4e.debug.breakpoints;x-internal:=true,
org.eclipse.lsp4e.debug.breakpoints;x-friends:="org.eclipse.lsp4e.test",
org.eclipse.lsp4e.debug.console;x-internal:=true,
org.eclipse.lsp4e.debug.debugmodel;x-friends:="org.eclipse.lsp4e.test",
org.eclipse.lsp4e.debug.launcher,
Expand Down
16 changes: 16 additions & 0 deletions org.eclipse.lsp4e.debug/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,20 @@
delegateClass="org.eclipse.lsp4e.debug.presentation.DAPWatchExpression">
</watchExpressionDelegate>
</extension>
<extension point="org.eclipse.debug.ui.detailPaneFactories">
<detailFactories
id="org.eclipse.lsp4e.debug.detailPaneFactory"
class="org.eclipse.lsp4e.debug.presentation.DSPBreakpointDetailPaneFactory">
<enablement>
<with variable="selection">
<count value="1"/>
<iterate>
<adapt type="org.eclipse.debug.core.model.IBreakpoint">
<instanceof value="org.eclipse.lsp4e.debug.breakpoints.DSPLineBreakpoint"/>
</adapt>
</iterate>
</with>
</enablement>
</detailFactories>
</extension>
</plugin>
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@
import org.eclipse.core.runtime.CoreException;
import org.eclipse.debug.core.model.IBreakpoint;
import org.eclipse.debug.core.model.LineBreakpoint;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.lsp4e.debug.DSPPlugin;

public class DSPLineBreakpoint extends LineBreakpoint {

public static final String ID = "org.eclipse.lsp4e.debug.breakpoints.markerType.lineBreakpoint";

/** Marker attribute key for a conditional expression. */
public static final String ATTR_CONDITION = "org.eclipse.lsp4e.debug.breakpoints.condition";

/** Marker attribute key for inline breakpoint column (1-based). */
public static final String ATTR_COLUMN = "org.eclipse.lsp4e.debug.breakpoints.column";

/** Marker attribute key for hit condition expression. */
public static final String ATTR_HIT_CONDITION = "org.eclipse.lsp4e.debug.breakpoints.hitCondition";

public DSPLineBreakpoint() {
}

Expand Down Expand Up @@ -48,4 +58,59 @@ public DSPLineBreakpoint(final IResource resource, String fileName, final int li
public String getModelIdentifier() {
return DSPPlugin.ID_DSP_DEBUG_MODEL;
}

/**
* @return the inline breakpoint column (1-based) or {@code -1} if unset.
*/
public int getColumn() {
final IMarker m = getMarker();
return m == null ? -1 : m.getAttribute(ATTR_COLUMN, -1);
}

/**
* Sets or clears the inline breakpoint column. Values <= 0 clear the column.
*/
public void setColumn(final int column) throws CoreException {
final IMarker m = getMarker();
if (m != null) {
m.setAttribute(ATTR_COLUMN, column <= 0 ? null : column);
}
}

/**
* @return the breakpoint condition or {@code null} if none.
*/
public @Nullable String getCondition() {
final IMarker m = getMarker();
return m == null ? null : m.getAttribute(ATTR_CONDITION, (String) null);
}

/**
* Sets or clears the breakpoint condition. A {@code null} or blank value clears
* the condition.
*/
public void setCondition(final @Nullable String condition) throws CoreException {
final IMarker m = getMarker();
if (m != null) {
m.setAttribute(ATTR_CONDITION, condition == null || condition.isBlank() ? null : condition);
}
}

/**
* @return the hit condition or {@code null} if none.
*/
public @Nullable String getHitCondition() {
final IMarker m = getMarker();
return m == null ? null : m.getAttribute(ATTR_HIT_CONDITION, (String) null);
}

/**
* Sets or clears the hit condition. A {@code null} or blank value clears it.
*/
public void setHitCondition(final @Nullable String hitCondition) throws CoreException {
final IMarker m = getMarker();
if (m != null) {
m.setAttribute(ATTR_HIT_CONDITION, hitCondition == null || hitCondition.isBlank() ? null : hitCondition);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.eclipse.debug.core.model.ILineBreakpoint;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.lsp4e.debug.DSPPlugin;
import org.eclipse.lsp4e.debug.breakpoints.DSPLineBreakpoint;
import org.eclipse.lsp4j.debug.BreakpointEventArguments;
import org.eclipse.lsp4j.debug.Capabilities;
import org.eclipse.lsp4j.debug.SetBreakpointsArguments;
Expand Down Expand Up @@ -179,6 +180,25 @@ private void addBreakpointToMap(IBreakpoint breakpoint) {
s -> new ArrayList<>());
final var sourceBreakpoint = new SourceBreakpoint();
sourceBreakpoint.setLine(lineNumber);

// inline (column) breakpoint support
final int column = marker.getAttribute(DSPLineBreakpoint.ATTR_COLUMN, -1);
if (column > 0) {
sourceBreakpoint.setColumn(column);
}

// conditional breakpoint support
final String condition = marker.getAttribute(DSPLineBreakpoint.ATTR_CONDITION, (String) null);
if (condition != null && !condition.isBlank()) {
sourceBreakpoint.setCondition(condition);
}

// hit condition support
final String hitCondition = marker.getAttribute(DSPLineBreakpoint.ATTR_HIT_CONDITION, (String) null);
if (hitCondition != null && !hitCondition.isBlank()) {
sourceBreakpoint.setHitCondition(hitCondition);
}

sourceBreakpoints.add(sourceBreakpoint);
}
}
Expand All @@ -202,7 +222,16 @@ private void deleteBreakpointFromMap(IBreakpoint breakpoint) {
List<SourceBreakpoint> bps = entry.getValue();
for (Iterator<SourceBreakpoint> iterator = bps.iterator(); iterator.hasNext();) {
SourceBreakpoint sourceBreakpoint = iterator.next();
if (Objects.equals(lineNumber, sourceBreakpoint.getLine())) {

// Match by line and (if present) column
Integer bpColumn = sourceBreakpoint.getColumn();
int markerColumn = lineBreakpoint.getMarker().getAttribute(DSPLineBreakpoint.ATTR_COLUMN, -1);
final boolean lineMatches = Objects.equals(lineNumber, sourceBreakpoint.getLine());
final boolean columnMatches = (markerColumn <= 0
&& (bpColumn == null || bpColumn.intValue() <= 0))
|| (markerColumn > 0 && bpColumn != null && bpColumn.intValue() == markerColumn);

if (lineMatches && columnMatches) {
iterator.remove();
}
}
Expand Down
Loading
Loading