Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Aggregate errors in xsd:import|include@schemaLocation for referenced grammar which have errors #1117

Merged
merged 1 commit into from
Oct 6, 2021
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
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,8 @@ public static void registerCodeActionParticipants(Map<String, ICodeActionPartici
SharedSettings sharedSettings) {
codeActions.put(ElementDeclUnterminated.getCode(), new ElementDeclUnterminatedCodeAction());
codeActions.put(EntityNotDeclared.getCode(), new EntityNotDeclaredCodeAction());
if (sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
if (sharedSettings != null
&& sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
codeActions.put(dtd_not_found.getCode(), new dtd_not_foundCodeAction());
}
ICodeActionParticipant fixMissingSpace = new FixMissingSpaceCodeAction();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.cvc_enumeration_validCodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.cvc_type_3_1_1CodeAction;
import org.eclipse.lemminx.extensions.contentmodel.participants.codeactions.schema_reference_4CodeAction;
import org.eclipse.lemminx.extensions.xsd.utils.XSDUtils;
import org.eclipse.lemminx.services.extensions.ICodeActionParticipant;
import org.eclipse.lemminx.services.extensions.diagnostics.IXMLErrorCode;
import org.eclipse.lemminx.settings.SharedSettings;
Expand Down Expand Up @@ -176,45 +177,63 @@ public static Range toLSPRange(XMLLocator location, XMLSchemaErrorCode code, Obj
}
return XMLPositionUtility.selectTrimmedText(offset, document);
}
case SchemaLocation: { //xml xsi:schemaLocation
case SchemaLocation: { // xml xsi:schemaLocation
SchemaLocation schemaLocation = document.getSchemaLocation();
DOMRange locationRange = schemaLocation.getAttr().getNodeAttrValue();
return locationRange != null ? XMLPositionUtility.createRange(locationRange) : null;
}
/**
* This error code occurs when an XSD file path is invalid in the following attributes:
* 1. xml-model href
* 2. xsi:schemaLocation
* 3. xsi:noNamespaceSchemaLocation
*/

case schema_reference_4: {
String hrefLocation = arguments.length == 1 ? (String) arguments[0] : null;
// Check if location comes from a xml-model/@href
DOMRange locationRange = XMLModelUtils.getHrefNode(document, hrefLocation);
if (locationRange == null) {
NoNamespaceSchemaLocation noNamespaceSchemaLocation = document.getNoNamespaceSchemaLocation();
if (noNamespaceSchemaLocation != null) {
locationRange = noNamespaceSchemaLocation.getAttr().getNodeAttrValue();
} else {
SchemaLocation schemaLocation = document.getSchemaLocation();
if (schemaLocation != null) {
String invalidSchemaPath = arguments[0] instanceof String ? (String) arguments[0] : null;
String grammarURI = arguments.length == 1 ? (String) arguments[0] : null;
if (DOMUtils.isXSD(document)) {
//
// This error code occurs in XSD document when an XSD file path is invalid in
// the following
// attributes:
// 1. xsd:import/@schemaLocation
// 2. xsd:include/@schemaLocation
//
// search grammar uri from xs:include/@schemaLocation or
// xs:import/@schemaLocation which reference the grammar URI
DOMAttr schemaLocationAttr = XSDUtils.findSchemaLocationAttrByURI(document, grammarURI);
if (schemaLocationAttr != null) {
return XMLPositionUtility.selectAttributeValue(schemaLocationAttr);
}
} else {
//
// This error code occurs when an XSD file path is invalid in the following
// attributes:
// 1. xml-model href
// 2. xsi:schemaLocation
// 3. xsi:noNamespaceSchemaLocation
//
// Check if location comes from a xml-model/@href
DOMRange locationRange = XMLModelUtils.getHrefNode(document, grammarURI);
if (locationRange == null) {
NoNamespaceSchemaLocation noNamespaceSchemaLocation = document.getNoNamespaceSchemaLocation();
if (noNamespaceSchemaLocation != null) {
locationRange = noNamespaceSchemaLocation.getAttr().getNodeAttrValue();
} else {
SchemaLocation schemaLocation = document.getSchemaLocation();
if (schemaLocation != null) {
String invalidSchemaPath = arguments[0] instanceof String ? (String) arguments[0] : null;

if (invalidSchemaPath != null) {
for (SchemaLocationHint locHintRange : schemaLocation.getSchemaLocationHints()) {
String expandedHint = getResolvedLocation(document.getDocumentURI(),
locHintRange.getHint());
if (invalidSchemaPath.equals(expandedHint)) {
return XMLPositionUtility.createRange(locHintRange);
if (invalidSchemaPath != null) {
for (SchemaLocationHint locHintRange : schemaLocation.getSchemaLocationHints()) {
String expandedHint = getResolvedLocation(document.getDocumentURI(),
locHintRange.getHint());
if (invalidSchemaPath.equals(expandedHint)) {
return XMLPositionUtility.createRange(locHintRange);
}
}
}
// Highlight entire attribute if finding the location hint fails
locationRange = schemaLocation.getAttr().getNodeAttrValue();
}
// Highlight entire attribute if finding the location hint fails
locationRange = schemaLocation.getAttr().getNodeAttrValue();
}
}
return locationRange != null ? XMLPositionUtility.createRange(locationRange) : null;
}
return locationRange != null ? XMLPositionUtility.createRange(locationRange) : null;
}
case cvc_attribute_3:
case cvc_complex_type_3_1:
Expand Down Expand Up @@ -279,7 +298,8 @@ public static void registerCodeActionParticipants(Map<String, ICodeActionPartici
codeActions.put(cvc_complex_type_2_1.getCode(), new cvc_complex_type_2_1CodeAction());
codeActions.put(TargetNamespace_1.getCode(), new TargetNamespace_1CodeAction());
codeActions.put(TargetNamespace_2.getCode(), new TargetNamespace_2CodeAction());
if (sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
if (sharedSettings != null
&& sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
codeActions.put(schema_reference_4.getCode(), new schema_reference_4CodeAction());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,8 @@ public static void registerCodeActionParticipants(Map<String, ICodeActionPartici
codeActions.put(ETagRequired.getCode(), new ETagRequiredCodeAction());
codeActions.put(RootElementTypeMustMatchDoctypedecl.getCode(),
new RootElementTypeMustMatchDoctypedeclCodeAction());
if (sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
if (sharedSettings != null
&& sharedSettings.getWorkspaceSettings().isResourceOperationSupported(ResourceOperationKind.Create)) {
codeActions.put(NoGrammarConstraints.getCode(), new NoGrammarConstraintsCodeAction());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,71 +12,47 @@
*/
package org.eclipse.lemminx.extensions.contentmodel.participants.diagnostics;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.xerces.xni.XMLLocator;
import org.eclipse.lemminx.commons.BadLocationException;
import org.eclipse.lemminx.dom.DOMDocument;
import org.eclipse.lemminx.dom.DOMRange;
import org.eclipse.lemminx.extensions.contentmodel.model.ContentModelManager;
import org.eclipse.lemminx.extensions.contentmodel.model.ReferencedGrammarInfo;
import org.eclipse.lemminx.extensions.contentmodel.participants.DTDErrorCode;
import org.eclipse.lemminx.extensions.contentmodel.participants.XMLSchemaErrorCode;
import org.eclipse.lemminx.extensions.contentmodel.participants.XMLSyntaxErrorCode;
import org.eclipse.lemminx.extensions.xerces.AbstractLSPErrorReporter;
import org.eclipse.lemminx.extensions.xerces.AbstractReferencedGrammarLSPErrorReporter;
import org.eclipse.lemminx.extensions.xerces.ReferencedGrammarDiagnosticsInfo;
import org.eclipse.lemminx.extensions.xsd.participants.XSDErrorCode;
import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager;
import org.eclipse.lemminx.utils.XMLPositionUtility;
import org.eclipse.lsp4j.Diagnostic;
import org.eclipse.lsp4j.DiagnosticRelatedInformation;
import org.eclipse.lsp4j.DiagnosticSeverity;
import org.eclipse.lsp4j.Location;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;

/**
* LSP error reporter for XML syntax and error grammar (XML Schema/DTD).
*
*/
public class LSPErrorReporterForXML extends AbstractLSPErrorReporter {
public class LSPErrorReporterForXML extends AbstractReferencedGrammarLSPErrorReporter {

private static final String XML_DIAGNOSTIC_SOURCE = "xml";

private final ContentModelManager contentModelManager;

private Set<ReferencedGrammarInfo> referencedGrammars;

private final Map<String, ReferencedGrammarDiagnosticsInfo> referencedGrammarDiagnosticsInfoCache;

private final boolean hasRelatedInformation;

public LSPErrorReporterForXML(DOMDocument xmlDocument, List<Diagnostic> diagnostics,
ContentModelManager contentModelManager, boolean hasRelatedInformation,
Map<String, ReferencedGrammarDiagnosticsInfo> referencedGrammarDiagnosticsInfoCache) {
super(XML_DIAGNOSTIC_SOURCE, xmlDocument, diagnostics, hasRelatedInformation);
this.contentModelManager = contentModelManager;
this.hasRelatedInformation = hasRelatedInformation;
this.referencedGrammarDiagnosticsInfoCache = referencedGrammarDiagnosticsInfoCache == null ? new HashMap<>()
: referencedGrammarDiagnosticsInfoCache;
super(XML_DIAGNOSTIC_SOURCE, xmlDocument, diagnostics, contentModelManager, hasRelatedInformation,
referencedGrammarDiagnosticsInfoCache);
}

/**
* Create the LSP range from the SAX error.
*
* @param location
* @param key
* @param arguments
* @param document
* @return the LSP range from the SAX error.
*/
@Override
protected Range toLSPRange(XMLLocator location, String key, Object[] arguments, String message,
DiagnosticSeverity diagnosticSeverity, boolean fatalError, DOMDocument document) {
String documentOrGrammarURI = location.getExpandedSystemId();
boolean errorForDocument = documentOrGrammarURI != null ? documentOrGrammarURI.endsWith(document.getDocumentURI()) : true;
DiagnosticSeverity diagnosticSeverity, boolean fatalError, DOMDocument document,
String documentOrGrammarURI, boolean errorForDocument) {
// try adjust positions for XML syntax error
XMLSyntaxErrorCode syntaxCode = XMLSyntaxErrorCode.get(key);
if (syntaxCode != null) {
Expand All @@ -87,7 +63,7 @@ protected Range toLSPRange(XMLLocator location, String key, Object[] arguments,
}
} else {
fillReferencedGrammarDiagnostic(location, key, arguments, message, diagnosticSeverity, fatalError,
document.getResolverExtensionManager(), syntaxCode, null, null, documentOrGrammarURI);
document.getResolverExtensionManager(), syntaxCode, null, null, null, documentOrGrammarURI);
return NO_RANGE;
}
} else {
Expand All @@ -109,7 +85,8 @@ protected Range toLSPRange(XMLLocator location, String key, Object[] arguments,
}
} else {
fillReferencedGrammarDiagnostic(location, key, arguments, message, diagnosticSeverity,
fatalError, document.getResolverExtensionManager(), null, dtdCode, null, documentOrGrammarURI);
fatalError, document.getResolverExtensionManager(), null, null, dtdCode, null,
documentOrGrammarURI);
return NO_RANGE;
}
} else {
Expand All @@ -121,98 +98,25 @@ protected Range toLSPRange(XMLLocator location, String key, Object[] arguments,
// Try to get the declared xsi:schemaLocation, xsi:noNamespaceLocation range
// which declares the XSD.
fillReferencedGrammarDiagnostic(location, key, arguments, message, diagnosticSeverity,
fatalError, document.getResolverExtensionManager(), null, null, xsdCode, documentOrGrammarURI);
fatalError, document.getResolverExtensionManager(), null, null, null, xsdCode,
documentOrGrammarURI);
return NO_RANGE;
}
}
}
}
if (!errorForDocument) {
// The error is not for the document, we ignore the diagnostic
return NO_RANGE;
}
return null;
}

/**
* Create a diagnostic root where XSD, DTD which have the error if needed and
* attach the error as related information if LSP client support it.
*
* @param location the Xerces location.
* @param key the Xerces error key
* @param arguments the Xerces error arguments
* @param message the Xerces error message
* @param diagnosticSeverity the the Xerces severity
* @param fatalError
* @param resolverExtensionManager the resolver
* @param syntaxCode the Syntax error code and null otherwise.
* @param dtdCode the DTD error code and null otherwise.
* @param xsdCode the XSD error code and null otherwise.
* @param grammarURI the referenced grammar URI.
*/
private void fillReferencedGrammarDiagnostic(XMLLocator location, String key, Object[] arguments, String message,
DiagnosticSeverity diagnosticSeverity, boolean fatalError,
URIResolverExtensionManager resolverExtensionManager, XMLSyntaxErrorCode syntaxCode, DTDErrorCode dtdCode,
XSDErrorCode xsdCode, String grammarURI) {
// Create diagnostic where DDT, XSD which have errors is declared if needed
ReferencedGrammarDiagnosticsInfo info = getReferencedGrammarDiagnosticsInfo(grammarURI,
resolverExtensionManager);
if (info.isFatalError()) {
// The last error was fatal, we ignore the other error to be consistent with
// XSD, DTD validator (when XSD or DTD is edited and validated) which stops the
// validation on the first fatal error.
return;
}
info.addError(fatalError);
if (hasRelatedInformation && info.getGrammarDocument() != null) {
DOMDocument grammarDocument = info.getGrammarDocument();
Range range = null;
if (dtdCode != null) {
range = DTDErrorCode.toLSPRange(location, dtdCode, arguments, grammarDocument);
} else if (xsdCode != null) {
range = XSDErrorCode.toLSPRange(location, xsdCode, arguments, grammarDocument);
} else {
range = XMLSyntaxErrorCode.toLSPRange(location, syntaxCode, arguments, grammarDocument);
}
if (range == null) {
range = createDefaultRange(location, grammarDocument);
}
if (range == null) {
try {
range = new Range(new Position(0, 0), grammarDocument.positionAt(grammarDocument.getEnd()));
} catch (BadLocationException e) {
}
}
DiagnosticRelatedInformation r = new DiagnosticRelatedInformation(
range != null ? new Location(grammarURI, range) : null, message);
info.addDiagnosticRelatedInformation(r);
}
}

/**
* Returns the referenced grammar diagnostics info from the given grammar URI.
*
* @param grammarURI the referenced grammar URI.
* @param resolverExtensionManager the resolver used to load the DOM document of
* the referenced grammar.
* @return
*/
private ReferencedGrammarDiagnosticsInfo getReferencedGrammarDiagnosticsInfo(String grammarURI,
URIResolverExtensionManager resolverExtensionManager) {
ReferencedGrammarDiagnosticsInfo info = referencedGrammarDiagnosticsInfoCache.get(grammarURI);
if (info == null) {
// Create diagnostic where DDT, XSD which have errors is declared
Range range = getReferencedGrammarRange(grammarURI);
String message = "";
Diagnostic diagnostic = super.addDiagnostic(range, message, DiagnosticSeverity.Error, null, null);
// Register the diagnostic as root diagnostic for the XSD, DTD grammar uri
info = new ReferencedGrammarDiagnosticsInfo(grammarURI, resolverExtensionManager, diagnostic);
referencedGrammarDiagnosticsInfoCache.put(grammarURI, info);
}
return info;
@Override
protected boolean isIgnoreFatalError(String key) {
// Don't stop the validation when there are
// * EntityNotDeclared error
return DTDErrorCode.EntityNotDeclared.name().equals(key);
}

private Range getReferencedGrammarRange(String grammarURI) {
@Override
protected Range getReferencedGrammarRange(String grammarURI) {
Set<ReferencedGrammarInfo> referencedGrammars = getReferencedGrammars();
for (ReferencedGrammarInfo referencedGrammarInfo : referencedGrammars) {
if (grammarURI.equals(referencedGrammarInfo.getResolvedURIInfo().getResolvedURI())) {
Expand All @@ -235,33 +139,4 @@ private Set<ReferencedGrammarInfo> getReferencedGrammars() {
return referencedGrammars = contentModelManager.getReferencedGrammarInfos(super.getDOMDocument());
}

@Override
protected boolean isIgnoreFatalError(String key) {
// Don't stop the validation when there are
// * EntityNotDeclared error
return DTDErrorCode.EntityNotDeclared.name().equals(key);
}

public void endReport() {
if (referencedGrammarDiagnosticsInfoCache.isEmpty()) {
return;
}
// When a XML is validated by a DTD or XSD which have syntax error, the XSD, DTD
// grammar is cached in the pool.
// This behavior is annoying because when XML is validate in the second time,
// Xerces uses the cached XSD, DTD grammar (which have syntax error)
// and the referenced grammar error disappear.

// To fix this problem, the grammar pool is updated by removing the referenced
// grammars which have problems.
LSPXMLGrammarPool grammarPool = (LSPXMLGrammarPool) contentModelManager.getGrammarPool();
if (grammarPool == null) {
return;
}
// Remove referenced grammar which have problem from the Xerces pool cache.
Set<String> grammarURIs = referencedGrammarDiagnosticsInfoCache.keySet();
for (String grammarURI : grammarURIs) {
grammarPool.removeGrammar(grammarURI);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLNamespacesSettings;
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLSchemaSettings;
import org.eclipse.lemminx.extensions.contentmodel.settings.XMLValidationSettings;
import org.eclipse.lemminx.extensions.xerces.ReferencedGrammarDiagnosticsInfo;
import org.eclipse.lemminx.services.extensions.diagnostics.LSPContentHandler;
import org.eclipse.lemminx.uriresolver.CacheResourceDownloadingException;
import org.eclipse.lemminx.uriresolver.IExternalGrammarLocationProvider;
Expand Down
Loading