summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/compiler-core/slang-json-native.cpp34
-rw-r--r--source/compiler-core/slang-json-value.h35
-rw-r--r--source/compiler-core/slang-language-server-protocol.cpp1
-rw-r--r--source/compiler-core/slang-language-server-protocol.h7
-rw-r--r--source/core/slang-rtti-info.h8
-rw-r--r--source/core/slang-rtti-util.cpp60
-rw-r--r--source/slang/slang-ast-decl.cpp3
-rw-r--r--source/slang/slang-ast-print.cpp12
-rw-r--r--source/slang/slang-check-decl.cpp101
-rw-r--r--source/slang/slang-check-impl.h5
-rw-r--r--source/slang/slang-check-modifier.cpp3
-rw-r--r--source/slang/slang-content-assist-info.h10
-rw-r--r--source/slang/slang-diagnostic-defs.h11
-rw-r--r--source/slang/slang-language-server-completion.cpp100
-rw-r--r--source/slang/slang-language-server-completion.h8
-rw-r--r--source/slang/slang-language-server.cpp28
-rw-r--r--source/slang/slang-parser.cpp18
-rw-r--r--source/slang/slang-workspace-version.h2
-rw-r--r--tests/diagnostics/interfaces/interface-override.slang28
-rw-r--r--tests/language-server/override-completion.slang16
20 files changed, 471 insertions, 19 deletions
diff --git a/source/compiler-core/slang-json-native.cpp b/source/compiler-core/slang-json-native.cpp
index 1f9a4dbc6..930aa611f 100644
--- a/source/compiler-core/slang-json-native.cpp
+++ b/source/compiler-core/slang-json-native.cpp
@@ -205,6 +205,22 @@ SlangResult JSONToNativeConverter::convert(const JSONValue& in, const RttiInfo*
*(UnownedStringSlice*)out = m_container->getString(in);
return SLANG_OK;
}
+ case RttiInfo::Kind::Optional:
+ {
+ if (in.getKind() == JSONValue::Kind::Null)
+ {
+ return SLANG_OK;
+ }
+ typedef List<Byte> Type;
+ const OptionalRttiInfo* optionalRttiInfo =
+ static_cast<const OptionalRttiInfo*>(rttiInfo);
+ auto hasValue = (uint8_t*)out;
+ *hasValue = 1;
+ return convert(
+ in,
+ optionalRttiInfo->m_elementType,
+ (uint8_t*)out + optionalRttiInfo->m_valueOffset);
+ }
case RttiInfo::Kind::List:
{
if (in.getKind() == JSONValue::Kind::Null)
@@ -441,6 +457,24 @@ SlangResult NativeToJSONConverter::convert(const RttiInfo* rttiInfo, const void*
{
return SLANG_E_NOT_IMPLEMENTED;
}
+ case RttiInfo::Kind::Optional:
+ {
+ const OptionalRttiInfo* optionalRttiInfo =
+ static_cast<const OptionalRttiInfo*>(rttiInfo);
+ auto hasValue = (const uint8_t*)in;
+ if (*hasValue)
+ {
+ return convert(
+ optionalRttiInfo->m_elementType,
+ (const uint8_t*)in + optionalRttiInfo->m_valueOffset,
+ out);
+ }
+ else
+ {
+ out = JSONValue::makeNull();
+ return SLANG_OK;
+ }
+ }
case RttiInfo::Kind::List:
{
const ListRttiInfo* listRttiInfo = static_cast<const ListRttiInfo*>(rttiInfo);
diff --git a/source/compiler-core/slang-json-value.h b/source/compiler-core/slang-json-value.h
index d9b17f1d5..ae0123d30 100644
--- a/source/compiler-core/slang-json-value.h
+++ b/source/compiler-core/slang-json-value.h
@@ -8,6 +8,8 @@
#include "slang-json-parser.h"
#include "slang-source-loc.h"
+#include <optional>
+
namespace Slang
{
@@ -439,6 +441,39 @@ protected:
List<JSONKeyValue> m_objectValues;
};
+template<typename T>
+class JSONOptional
+{
+public:
+ bool hasValue = false;
+ T value;
+ JSONOptional() = default;
+ JSONOptional(std::nullopt_t) {}
+ JSONOptional(const T& inValue)
+ : hasValue(true), value(inValue)
+ {
+ }
+};
+
+template<typename T>
+struct GetRttiInfo<JSONOptional<T>>
+{
+ static const OptionalRttiInfo _make()
+ {
+ OptionalRttiInfo info;
+ info.init<JSONOptional<T>>(RttiInfo::Kind::Optional);
+ info.m_elementType = GetRttiInfo<T>::get();
+ info.m_valueOffset = (uint32_t)offsetof(JSONOptional<T>, value);
+ return info;
+ }
+ static const RttiInfo* get()
+ {
+ static const OptionalRttiInfo g_info = _make();
+ return &g_info;
+ }
+};
+
+
class JSONBuilder : public JSONListener
{
public:
diff --git a/source/compiler-core/slang-language-server-protocol.cpp b/source/compiler-core/slang-language-server-protocol.cpp
index 9a382e756..2530120d3 100644
--- a/source/compiler-core/slang-language-server-protocol.cpp
+++ b/source/compiler-core/slang-language-server-protocol.cpp
@@ -522,6 +522,7 @@ static const StructRttiInfo _makeCompletionItemRtti()
builder.addField("label", &obj.label, StructRttiInfo::Flag::Optional);
builder.addField("detail", &obj.detail, StructRttiInfo::Flag::Optional);
builder.addField("kind", &obj.kind, StructRttiInfo::Flag::Optional);
+ builder.addField("sortText", &obj.sortText, StructRttiInfo::Flag::Optional);
builder.addField("documentation", &obj.documentation, StructRttiInfo::Flag::Optional);
builder.addField("data", &obj.data, StructRttiInfo::Flag::Optional);
builder.addField("commitCharacters", &obj.commitCharacters, StructRttiInfo::Flag::Optional);
diff --git a/source/compiler-core/slang-language-server-protocol.h b/source/compiler-core/slang-language-server-protocol.h
index d96099da6..ff933e867 100644
--- a/source/compiler-core/slang-language-server-protocol.h
+++ b/source/compiler-core/slang-language-server-protocol.h
@@ -611,6 +611,13 @@ struct CompletionItem
String detail;
/**
+ * A string that should be used when comparing this item
+ * with other items. When omitted the label is used
+ * as the sort text for this item.
+ */
+ JSONOptional<String> sortText;
+
+ /**
* A human-readable string that represents a doc-comment.
*/
MarkupContent documentation;
diff --git a/source/core/slang-rtti-info.h b/source/core/slang-rtti-info.h
index 01c042511..58e557dd0 100644
--- a/source/core/slang-rtti-info.h
+++ b/source/core/slang-rtti-info.h
@@ -181,7 +181,7 @@ struct RttiInfo
Enum,
List,
Dictionary,
-
+ Optional,
CountOf,
};
@@ -296,6 +296,12 @@ SLANG_FORCE_INLINE StructRttiInfo::Flags combine(
return StructRttiInfo::Flags(defaultValue) | flags;
}
+struct OptionalRttiInfo : public RttiInfo
+{
+ const RttiInfo* m_elementType;
+ uint32_t m_valueOffset;
+};
+
struct ListRttiInfo : public RttiInfo
{
const RttiInfo* m_elementType;
diff --git a/source/core/slang-rtti-util.cpp b/source/core/slang-rtti-util.cpp
index 92f45a3bf..01571cab1 100644
--- a/source/core/slang-rtti-util.cpp
+++ b/source/core/slang-rtti-util.cpp
@@ -336,6 +336,7 @@ RttiTypeFuncs RttiUtil::getDefaultTypeFuncs(const RttiInfo* rttiInfo)
case RttiInfo::Kind::List:
return ListFuncs::getFuncs();
case RttiInfo::Kind::Struct:
+ case RttiInfo::Kind::Optional:
return StructArrayFuncs::getFuncs();
default:
break;
@@ -556,6 +557,10 @@ static bool _isStructDefault(const StructRttiInfo* type, const void* src)
const OtherRttiInfo* otherRttiInfo = static_cast<const OtherRttiInfo*>(rttiInfo);
return otherRttiInfo->m_isDefaultFunc && otherRttiInfo->m_isDefaultFunc(rttiInfo, src);
}
+ case RttiInfo::Kind::Optional:
+ {
+ return *(const bool*)src == (_getIntDefaultValue(defaultValue) != 0);
+ }
default:
{
return false;
@@ -660,6 +665,11 @@ static bool _isStructDefault(const StructRttiInfo* type, const void* src)
return true;
}
+ case RttiInfo::Kind::Optional:
+ {
+ const OptionalRttiInfo* optionalRttiInfo = static_cast<const OptionalRttiInfo*>(type);
+ return canMemCpy(optionalRttiInfo->m_elementType);
+ }
default:
{
return type->isBuiltIn();
@@ -723,6 +733,11 @@ static bool _isStructDefault(const StructRttiInfo* type, const void* src)
return true;
}
+ case RttiInfo::Kind::Optional:
+ {
+ const OptionalRttiInfo* optionalRttiInfo = static_cast<const OptionalRttiInfo*>(type);
+ return canZeroInit(optionalRttiInfo->m_elementType);
+ }
default:
{
return type->isBuiltIn();
@@ -783,6 +798,11 @@ static bool _isStructDefault(const StructRttiInfo* type, const void* src)
} while (structRttiInfo);
return false;
}
+ case RttiInfo::Kind::Optional:
+ {
+ const OptionalRttiInfo* optionalRttiInfo = static_cast<const OptionalRttiInfo*>(type);
+ return hasDtor(optionalRttiInfo->m_elementType);
+ }
default:
{
return !type->isBuiltIn();
@@ -894,6 +914,19 @@ static bool _isStructDefault(const StructRttiInfo* type, const void* src)
return;
}
+ case RttiInfo::Kind::Optional:
+ {
+ const OptionalRttiInfo* optionalRttiInfo =
+ static_cast<const OptionalRttiInfo*>(rttiInfo);
+ ctorArray(typeMap, GetRttiInfo<bool>::get(), dst, stride, count);
+ ctorArray(
+ typeMap,
+ optionalRttiInfo->m_elementType,
+ dst + optionalRttiInfo->m_valueOffset,
+ stride,
+ count);
+ return;
+ }
}
SLANG_ASSERT(!"Unexpected");
@@ -1005,6 +1038,20 @@ static bool _isStructDefault(const StructRttiInfo* type, const void* src)
return;
}
+ case RttiInfo::Kind::Optional:
+ {
+ const OptionalRttiInfo* optionalRttiInfo =
+ static_cast<const OptionalRttiInfo*>(rttiInfo);
+ copyArray(typeMap, GetRttiInfo<bool>::get(), dst, src, stride, count);
+ copyArray(
+ typeMap,
+ optionalRttiInfo->m_elementType,
+ dst + optionalRttiInfo->m_valueOffset,
+ src + optionalRttiInfo->m_valueOffset,
+ stride,
+ count);
+ return;
+ }
}
SLANG_ASSERT(!"Unexpected");
@@ -1091,6 +1138,19 @@ static bool _isStructDefault(const StructRttiInfo* type, const void* src)
return;
}
+ case RttiInfo::Kind::Optional:
+ {
+ const OptionalRttiInfo* optionalRttiInfo =
+ static_cast<const OptionalRttiInfo*>(rttiInfo);
+ dtorArray(typeMap, GetRttiInfo<bool>::get(), dst, stride, count);
+ dtorArray(
+ typeMap,
+ optionalRttiInfo->m_elementType,
+ dst + optionalRttiInfo->m_valueOffset,
+ stride,
+ count);
+ return;
+ }
}
SLANG_ASSERT(!"Unexpected");
diff --git a/source/slang/slang-ast-decl.cpp b/source/slang/slang-ast-decl.cpp
index 5152f0901..f37ebef48 100644
--- a/source/slang/slang-ast-decl.cpp
+++ b/source/slang/slang-ast-decl.cpp
@@ -42,7 +42,8 @@ bool isInterfaceRequirement(Decl* decl)
{
if (as<InterfaceDecl>(ancestor))
return true;
-
+ if (as<InterfaceDefaultImplDecl>(ancestor))
+ return false;
if (as<ExtensionDecl>(ancestor))
return false;
}
diff --git a/source/slang/slang-ast-print.cpp b/source/slang/slang-ast-print.cpp
index 7b239c88c..94864b815 100644
--- a/source/slang/slang-ast-print.cpp
+++ b/source/slang/slang-ast-print.cpp
@@ -1314,16 +1314,14 @@ void ASTPrinter::addGenericParams(const DeclRef<GenericDecl>& genericDeclRef)
first = false;
{
- ScopePart scopePart(this, Part::Type::GenericParamValue);
- sb << getText(genericValParam.getName());
- }
-
- sb << ":";
-
- {
ScopePart scopePart(this, Part::Type::GenericParamValueType);
addType(getType(m_astBuilder, genericValParam));
}
+ sb << " ";
+ {
+ ScopePart scopePart(this, Part::Type::GenericParamValue);
+ sb << getText(genericValParam.getName());
+ }
}
else if (auto genericTypePackParam = paramDeclRef.as<GenericTypePackParamDecl>())
{
diff --git a/source/slang/slang-check-decl.cpp b/source/slang/slang-check-decl.cpp
index 23458c94a..f0cd32e74 100644
--- a/source/slang/slang-check-decl.cpp
+++ b/source/slang/slang-check-decl.cpp
@@ -353,6 +353,8 @@ struct SemanticsDeclHeaderVisitor : public SemanticsDeclVisitorBase,
void checkDifferentiableCallableCommon(CallableDecl* decl);
+ void checkInterfaceRequirement(Decl* decl);
+
void checkCallableDeclCommon(CallableDecl* decl);
void visitFuncDecl(FuncDecl* funcDecl);
@@ -7800,11 +7802,84 @@ void SemanticsVisitor::checkAggTypeConformance(AggTypeDecl* decl)
innerMember,
Diagnostics::overrideModifierNotOverridingBaseDecl,
innerMember);
+
+ if (getShared()->isInLanguageServer() &&
+ member->getName() == getShared()->getSession()->getCompletionRequestTokenName())
+ {
+ // If we encountered a completion request for a decl name where an 'override'
+ // keyword is specified, we should suggest all the base decls that can be
+ // overridden, but have not been overridden yet.
+ //
+ calcOverridableCompletionCandidates(type, decl, member);
+ }
}
}
}
}
+
+void SemanticsVisitor::calcOverridableCompletionCandidates(
+ Type* aggType,
+ ContainerDecl* aggTypeDecl,
+ Decl* memberDecl)
+{
+ // We are in language server and the user requested to list
+ // all base interface methods that can be overrided in a conforming type.
+ // Collect all base interfaces methods that hasn't been overridden and
+ // suggest them as completion candidates.
+ if (as<InterfaceDecl>(aggTypeDecl))
+ {
+ // If the aggType is an interface, we don't have any base methods to suggest.
+ return;
+ }
+ auto inheritanceInfo = getShared()->getInheritanceInfo(aggType);
+ HashSet<Decl*> overridenDecls;
+ for (auto member : aggTypeDecl->getMembers())
+ {
+ member = maybeGetInner(member);
+ if (!as<FuncDecl>(member))
+ continue;
+ if (auto overridedDeclModifier = member->findModifier<IsOverridingModifier>())
+ {
+ overridenDecls.add(overridedDeclModifier->overridedDecl);
+ }
+ }
+ auto& contentAssistInfo = getShared()->getLinkage()->contentAssistInfo;
+ contentAssistInfo.completionSuggestions.scopeKind = CompletionSuggestions::ScopeKind::Decl;
+ auto varDeclBase = as<VarDeclBase>(memberDecl);
+ contentAssistInfo.completionSuggestions.formatMode =
+ varDeclBase ? CompletionSuggestions::FormatMode::FuncSignatureWithoutReturnType
+ : CompletionSuggestions::FormatMode::FullSignature;
+
+ List<LookupResultItem> candidateItems;
+ for (auto facet : inheritanceInfo.facets)
+ {
+ // Extensions don't contribute overridable members.
+ if (facet->kind == Facet::Kind::Extension)
+ continue;
+
+ auto interfaceDecl = facet->getDeclRef().as<InterfaceDecl>();
+ if (!interfaceDecl)
+ continue;
+ for (auto requirement : interfaceDecl.getDecl()->getMembers())
+ {
+ requirement = maybeGetInner(requirement);
+ if (!as<FuncDecl>(requirement))
+ continue;
+ if (!overridenDecls.contains(requirement))
+ {
+ auto requirementDeclRef = DeclRef<Decl>(requirement);
+ requirementDeclRef =
+ createDefaultSubstitutionsIfNeeded(m_astBuilder, this, requirementDeclRef);
+ candidateItems.add(LookupResultItem(requirementDeclRef));
+ }
+ }
+ }
+ // Insert overridable candidates at the front of the list, so they are preferred over
+ // the completion results from checking ordinary type exprs.
+ contentAssistInfo.completionSuggestions.candidateItems.insertRange(0, candidateItems);
+}
+
void SemanticsDeclBasesVisitor::_validateCrossModuleInheritance(
AggTypeDeclBase* decl,
InheritanceDecl* inheritanceDecl)
@@ -10209,6 +10284,26 @@ void SemanticsDeclHeaderVisitor::checkDifferentiableCallableCommon(CallableDecl*
}
}
+void SemanticsDeclHeaderVisitor::checkInterfaceRequirement(Decl* decl)
+{
+ if (isInterfaceRequirement(decl))
+ {
+ if (auto funcBase = as<FunctionDeclBase>(decl))
+ {
+ if (!as<FuncDecl>(decl) && funcBase->body != nullptr)
+ {
+ getSink()->diagnose(decl, Diagnostics::nonMethodInterfaceRequirementCannotHaveBody);
+ return;
+ }
+ }
+ // Interface requirement cannot be `override`.
+ if (decl->hasModifier<OverrideModifier>())
+ {
+ getSink()->diagnose(decl, Diagnostics::interfaceRequirementCannotBeOverride);
+ }
+ }
+}
+
void SemanticsDeclHeaderVisitor::checkCallableDeclCommon(CallableDecl* decl)
{
for (auto paramDecl : decl->getParameters())
@@ -10238,6 +10333,7 @@ void SemanticsDeclHeaderVisitor::checkCallableDeclCommon(CallableDecl* decl)
}
}
+ checkInterfaceRequirement(decl);
checkVisibility(decl);
}
@@ -10642,7 +10738,10 @@ void SemanticsDeclHeaderVisitor::visitAbstractStorageDeclCommon(ContainerDecl* d
//
bool anyAccessors = decl->getMembersOfType<AccessorDecl>().isNonEmpty();
-
+ for (auto accessor : decl->getMembersOfType<AccessorDecl>())
+ {
+ checkInterfaceRequirement(accessor);
+ }
if (!anyAccessors)
{
GetterDecl* getterDecl = m_astBuilder->create<GetterDecl>();
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 365f21b1c..d4d431914 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -2100,6 +2100,11 @@ public:
void checkExtensionConformance(ExtensionDecl* decl);
+ void calcOverridableCompletionCandidates(
+ Type* aggType,
+ ContainerDecl* aggTypeDecl,
+ Decl* memberDecl);
+
void checkAggTypeConformance(AggTypeDecl* decl);
bool isIntegerBaseType(BaseType baseType);
diff --git a/source/slang/slang-check-modifier.cpp b/source/slang/slang-check-modifier.cpp
index 2a4707db5..d135744af 100644
--- a/source/slang/slang-check-modifier.cpp
+++ b/source/slang/slang-check-modifier.cpp
@@ -2149,6 +2149,9 @@ void SemanticsVisitor::checkModifiers(ModifiableSyntaxNode* syntaxNode)
// an error if the modifier is not allowed on the declaration.
if (as<SharedModifiers>(modifier))
ignoreUnallowedModifier = true;
+ else if (
+ getLinkage()->contentAssistInfo.checkingMode == ContentAssistCheckingMode::Completion)
+ ignoreUnallowedModifier = true;
// may return a list of modifiers
auto checkedModifier = checkModifier(modifier, syntaxNode, ignoreUnallowedModifier);
diff --git a/source/slang/slang-content-assist-info.h b/source/slang/slang-content-assist-info.h
index 6d4503cc5..77102bf66 100644
--- a/source/slang/slang-content-assist-info.h
+++ b/source/slang/slang-content-assist-info.h
@@ -22,7 +22,17 @@ struct CompletionSuggestions
HLSLSemantics,
Capabilities
};
+
+ enum class FormatMode
+ {
+ Name,
+ FullSignature,
+ FuncSignatureWithoutReturnType
+ };
+
ScopeKind scopeKind = ScopeKind::Invalid;
+ FormatMode formatMode = FormatMode::Name;
+
List<LookupResultItem> candidateItems;
Type* swizzleBaseType = nullptr;
IntegerLiteralValue elementCount[2] = {0, 0};
diff --git a/source/slang/slang-diagnostic-defs.h b/source/slang/slang-diagnostic-defs.h
index 64b47ece8..d54e1a3e0 100644
--- a/source/slang/slang-diagnostic-defs.h
+++ b/source/slang/slang-diagnostic-defs.h
@@ -1507,6 +1507,17 @@ DIAGNOSTIC(
"requirement in the form of a simple value must be declared as 'static const'.")
DIAGNOSTIC(30310, Error, typeIsNotDifferentiable, "type '$0' is not differentiable.")
+DIAGNOSTIC(
+ 30311,
+ Error,
+ nonMethodInterfaceRequirementCannotHaveBody,
+ "non-method interface requirement cannot have a body.")
+DIAGNOSTIC(
+ 30312,
+ Error,
+ interfaceRequirementCannotBeOverride,
+ "interface requirement cannot override a base declaration.")
+
// Interop
DIAGNOSTIC(
30400,
diff --git a/source/slang/slang-language-server-completion.cpp b/source/slang/slang-language-server-completion.cpp
index 240102a22..59aa3e4d8 100644
--- a/source/slang/slang-language-server-completion.cpp
+++ b/source/slang/slang-language-server-completion.cpp
@@ -5,6 +5,7 @@
#include "../core/slang-char-util.h"
#include "../core/slang-file-system.h"
#include "slang-ast-all.h"
+#include "slang-ast-print.h"
#include "slang-check-impl.h"
#include "slang-language-server-ast-lookup.h"
#include "slang-language-server.h"
@@ -22,7 +23,7 @@ static const char* kDeclKeywords[] = {
"protected", "typedef", "typealias", "uniform", "export", "groupshared",
"extension", "associatedtype", "namespace", "This", "using", "__generic",
"__exported", "import", "enum", "cbuffer", "tbuffer", "func",
- "functype", "typename", "each", "expand", "where"};
+ "functype", "typename", "each", "expand", "where", "override"};
static const char* kStmtKeywords[] = {
"if",
"else",
@@ -96,6 +97,8 @@ static const char* hlslSemanticNames[] = {
"packoffset",
"read",
"write",
+ "SV_BaseInstanceID",
+ "SV_BaryCentrics",
"SV_ClipDistance",
"SV_CullDistance",
"SV_Coverage",
@@ -126,6 +129,8 @@ static const char* hlslSemanticNames[] = {
"SV_VertexID",
"SV_ViewID",
"SV_ViewportArrayIndex",
+ "SV_VulkanVertexID",
+ "SV_VulkanInstanceID",
"SV_ShadingRate",
"SV_StartVertexLocation",
"SV_StartInstanceLocation",
@@ -511,10 +516,72 @@ LanguageServerResult<CompletionResult> CompletionContext::tryCompleteMemberAndSy
return collectMembersAndSymbols();
}
+String CompletionContext::formatDeclForCompletion(
+ DeclRef<Decl> declRef,
+ ASTBuilder* astBuilder,
+ CompletionSuggestions::FormatMode formatMode,
+ int& outNameStart)
+{
+ outNameStart = 0;
+ switch (formatMode)
+ {
+ case CompletionSuggestions::FormatMode::Name:
+ return getText(declRef.getDecl()->getName());
+ default:
+ break;
+ }
+
+ ASTPrinter printer(astBuilder, ASTPrinter::OptionFlag::ParamNames);
+ if (auto genDecl = as<GenericDecl>(declRef))
+ declRef = astBuilder->getMemberDeclRef(genDecl, genDecl.getDecl()->inner);
+ auto callableDecl = as<CallableDecl>(declRef);
+ if (!callableDecl)
+ return String();
+ if (formatMode == CompletionSuggestions::FormatMode::FullSignature)
+ {
+ printer.addType(callableDecl.getDecl()->returnType.type);
+ printer.getStringBuilder() << " ";
+ }
+ outNameStart = (int)printer.getStringBuilder().getLength();
+ printer.getStringBuilder() << getText(declRef.getDecl()->getName());
+ auto outerGeneric = as<GenericDecl>(declRef.getParent());
+ if (outerGeneric)
+ {
+ printer.addGenericParams(outerGeneric);
+ }
+ printer.addDeclParams(declRef);
+ if (callableDecl.getDecl()->errorType.type != astBuilder->getBottomType() &&
+ callableDecl.getDecl()->errorType.type != astBuilder->getErrorType())
+ {
+ printer.getStringBuilder() << " throws ";
+ printer.addType(callableDecl.getDecl()->errorType);
+ }
+ if (outerGeneric)
+ {
+ for (auto constraint :
+ outerGeneric.getDecl()->getMembersOfType<GenericTypeConstraintDecl>())
+ {
+ printer.getStringBuilder() << "\n";
+ bool indentUsingTab = indent.startsWith("\t");
+ if (indentUsingTab)
+ printer.getStringBuilder() << "\t";
+ else
+ printer.getStringBuilder() << " ";
+ printer.getStringBuilder() << "where ";
+ printer.addType(constraint->sub.type);
+ if (constraint->isEqualityConstraint)
+ printer.getStringBuilder() << " == ";
+ else
+ printer.getStringBuilder() << " : ";
+ printer.addType(constraint->sup.type);
+ }
+ }
+ return printer.getString();
+}
+
CompletionResult CompletionContext::collectMembersAndSymbols()
{
List<LanguageServerProtocol::CompletionItem> result;
-
auto linkage = version->linkage;
if (linkage->contentAssistInfo.completionSuggestions.scopeKind ==
CompletionSuggestions::ScopeKind::Swizzle)
@@ -563,7 +630,14 @@ CompletionResult CompletionContext::collectMembersAndSymbols()
if (!member->getName())
continue;
LanguageServerProtocol::CompletionItem item;
- item.label = member->getName()->text;
+ int nameStart = 0;
+ item.label = formatDeclForCompletion(
+ suggestedItem.declRef,
+ linkage->m_astBuilder,
+ linkage->contentAssistInfo.completionSuggestions.formatMode,
+ nameStart);
+ if (item.label.getLength() == 0)
+ continue;
item.kind = LanguageServerProtocol::kCompletionItemKindKeyword;
if (as<TypeConstraintDecl>(member))
{
@@ -627,7 +701,27 @@ CompletionResult CompletionContext::collectMembersAndSymbols()
item.kind = LanguageServerProtocol::kCompletionItemKindClass;
}
item.data = String(i);
+ if (linkage->contentAssistInfo.completionSuggestions.formatMode !=
+ CompletionSuggestions::FormatMode::Name)
+ {
+ item.sortText =
+ (StringBuilder() << i << ":" << getText(member->getName())).produceString();
+ }
+
result.add(item);
+ if (nameStart > 1)
+ {
+ // If the completion item is for a full function signature, add the return type part too
+ // as a separate item.
+ item.label = item.label.getUnownedSlice().head(nameStart - 1);
+ item.kind = LanguageServerProtocol::kCompletionItemKindStruct;
+ item.sortText =
+ (StringBuilder()
+ << linkage->contentAssistInfo.completionSuggestions.candidateItems.getCount()
+ << ":" << item.label)
+ .produceString();
+ result.add(item);
+ }
}
if (addKeywords)
{
diff --git a/source/slang/slang-language-server-completion.h b/source/slang/slang-language-server-completion.h
index fab1c8a75..513538ac8 100644
--- a/source/slang/slang-language-server-completion.h
+++ b/source/slang/slang-language-server-completion.h
@@ -42,7 +42,10 @@ struct CompletionContext
CommitCharacterBehavior commitCharacterBehavior;
Int line;
Int col;
+ String indent;
+ // The token range of original request, in 0-based UTF16 code units.
+ LanguageServerProtocol::Range requestRange;
LanguageServerResult<CompletionResult> tryCompleteMemberAndSymbol();
LanguageServerResult<CompletionResult> tryCompleteHLSLSemantic();
LanguageServerResult<CompletionResult> tryCompleteAttributes();
@@ -55,6 +58,11 @@ struct CompletionContext
CompletionResult collectMembersAndSymbols();
+ String formatDeclForCompletion(
+ DeclRef<Decl> decl,
+ ASTBuilder* astBuilder,
+ CompletionSuggestions::FormatMode formatMode,
+ int& outNameStart);
void createSwizzleCandidates(
List<LanguageServerProtocol::CompletionItem>& result,
Type* type,
diff --git a/source/slang/slang-language-server.cpp b/source/slang/slang-language-server.cpp
index cd9818f65..6daeb75ad 100644
--- a/source/slang/slang-language-server.cpp
+++ b/source/slang/slang-language-server.cpp
@@ -1157,15 +1157,17 @@ LanguageServerResult<CompletionResult> LanguageServerCore::completion(
}
// Ajust cursor position to the beginning of the current/last identifier.
+ Index firstTokenCharOffset = cursorOffset;
cursorOffset--;
while (cursorOffset > 0 && _isIdentifierChar(doc->getText()[cursorOffset]))
{
+ firstTokenCharOffset = cursorOffset;
cursorOffset--;
}
// Never show suggestions when the user is typing a number.
- if (cursorOffset + 1 >= 0 && cursorOffset + 1 < doc->getText().getLength() &&
- CharUtil::isDigit(doc->getText()[cursorOffset + 1]))
+ if (firstTokenCharOffset >= 0 && firstTokenCharOffset < doc->getText().getLength() &&
+ CharUtil::isDigit(doc->getText()[firstTokenCharOffset]))
{
return std::nullopt;
}
@@ -1180,12 +1182,32 @@ LanguageServerResult<CompletionResult> LanguageServerCore::completion(
version->linkage->contentAssistInfo.cursorCol = utf8Col;
Slang::CompletionContext context;
context.server = this;
- context.cursorOffset = cursorOffset;
+ context.cursorOffset = firstTokenCharOffset;
context.version = version;
context.doc = doc.Ptr();
context.canonicalPath = canonicalPath.getUnownedSlice();
context.line = utf8Line;
context.col = utf8Col;
+ Int firstNonWhiteSpace = 0;
+ auto lineContent = doc->getLine(utf8Line);
+ while (firstNonWhiteSpace < lineContent.getLength() &&
+ CharUtil::isWhitespace(lineContent[firstNonWhiteSpace]))
+ firstNonWhiteSpace++;
+ context.indent = doc->getLine(utf8Line).head(firstNonWhiteSpace);
+ doc->offsetToLineCol(firstTokenCharOffset, utf8Line, utf8Col);
+ doc->oneBasedUTF8LocToZeroBasedUTF16Loc(
+ utf8Line,
+ utf8Col,
+ context.requestRange.start.line,
+ context.requestRange.start.character);
+
+ auto utf8TokenLen = doc->getTokenLength(firstTokenCharOffset);
+ doc->oneBasedUTF8LocToZeroBasedUTF16Loc(
+ utf8Line,
+ utf8Col + utf8TokenLen,
+ context.requestRange.end.line,
+ context.requestRange.end.character);
+
context.commitCharacterBehavior = m_commitCharacterBehavior;
if (args.context.triggerKind == kCompletionTriggerKindTriggerCharacter &&
(args.context.triggerCharacter == " " || args.context.triggerCharacter == "[" ||
diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp
index 9727140f3..a019f97c4 100644
--- a/source/slang/slang-parser.cpp
+++ b/source/slang/slang-parser.cpp
@@ -1443,8 +1443,7 @@ static NameLoc ParseDeclName(Parser* parser)
}
else
{
- nameToken = parser->ReadToken(TokenType::Identifier);
- return NameLoc(nameToken);
+ return expectIdentifier(parser);
}
}
@@ -2037,6 +2036,7 @@ static RefPtr<Declarator> parseDirectAbstractDeclarator(
switch (parser->tokenReader.peekTokenType())
{
case TokenType::Identifier:
+ case TokenType::CompletionRequest:
{
auto nameDeclarator = new NameDeclarator();
nameDeclarator->flavor = Declarator::Flavor::name;
@@ -5048,6 +5048,20 @@ static DeclBase* ParseDeclWithModifiers(
decl->loc = loc;
}
break;
+ case TokenType::CompletionRequest:
+ {
+ if (modifiers.hasModifier<OverrideModifier>())
+ {
+ auto resultDecl = parser->astBuilder->create<EmptyDecl>();
+ resultDecl->nameAndLoc = expectIdentifier(parser);
+ decl = resultDecl;
+ }
+ else
+ {
+ decl = ParseDeclaratorDecl(parser, containerDecl, modifiers);
+ }
+ }
+ break;
// If nothing else matched, we try to parse an "ordinary" declarator-based declaration
default:
decl = ParseDeclaratorDecl(parser, containerDecl, modifiers);
diff --git a/source/slang/slang-workspace-version.h b/source/slang/slang-workspace-version.h
index 1a12a2b42..71f5af5c6 100644
--- a/source/slang/slang-workspace-version.h
+++ b/source/slang/slang-workspace-version.h
@@ -102,7 +102,7 @@ public:
col = Index(offset - getLineStart(lines[line - 1])) + 1;
}
if (line > 0 && line <= lines.getCount())
- col = UTF8Util::calcCodePointCount(lines[line - 1].head(col));
+ col = UTF8Util::calcCodePointCount(lines[line - 1].head(col - 1)) + 1;
}
// Get line from 1-based index.
diff --git a/tests/diagnostics/interfaces/interface-override.slang b/tests/diagnostics/interfaces/interface-override.slang
new file mode 100644
index 000000000..12539895b
--- /dev/null
+++ b/tests/diagnostics/interfaces/interface-override.slang
@@ -0,0 +1,28 @@
+//DIAGNOSTIC_TEST:SIMPLE(filecheck=CHECK):
+
+interface IBase
+{
+ int f();
+}
+
+interface IDerived : IBase
+{
+ //CHECK-DAG: ([[# @LINE+1]]): error 30311:
+ __init() {}
+
+ property int value{
+ get;
+
+ //CHECK-DAG: ([[# @LINE+1]]): error 30311:
+ set{ }
+ }
+
+ __subscript(int index)->int
+ {
+ //CHECK-DAG: ([[# @LINE+1]]): error 30311:
+ get{}
+ }
+
+ //CHECK-DAG: ([[# @LINE+1]]): error 30312:
+ override int f() {}
+} \ No newline at end of file
diff --git a/tests/language-server/override-completion.slang b/tests/language-server/override-completion.slang
new file mode 100644
index 000000000..7cfd52b3e
--- /dev/null
+++ b/tests/language-server/override-completion.slang
@@ -0,0 +1,16 @@
+//TEST:LANG_SERVER(filecheck=CHECK):
+interface IBar{}
+interface IFoo
+{
+ int eval();
+ void execute<int z,T:IBar>(int x);
+}
+
+struct Impl : IFoo
+{
+//COMPLETE:12,14
+ override
+}
+
+//CHECK-DAG: int eval
+//CHECK-DAG: void execute<int z, T> \ No newline at end of file